blob: 5cc55a465e542befb5e0de32466e42c130c2d6e5 [file] [log] [blame]
// Copyright 2019 The Chromium OS 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 <limits>
#include <memory>
#include <type_traits>
#include <base/bind.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/stringprintf.h>
#include <base/threading/thread_task_runner_handle.h>
#include <brillo/grpc/async_grpc_client.h>
#include <gmock/gmock.h>
#include <google/protobuf/util/message_differencer.h>
#include <gtest/gtest.h>
#include "diagnostics/common/protobuf_test_utils.h"
#include "diagnostics/constants/grpc_constants.h"
#include "diagnostics/dpsl/internal/dpsl_global_context_impl.h"
#include "diagnostics/dpsl/internal/dpsl_rpc_server_impl.h"
#include "diagnostics/dpsl/internal/dpsl_thread_context_impl.h"
#include "diagnostics/dpsl/internal/test_dpsl_background_thread.h"
#include "diagnostics/dpsl/public/dpsl_global_context.h"
#include "diagnostics/dpsl/public/dpsl_rpc_handler.h"
#include "diagnostics/dpsl/public/dpsl_rpc_server.h"
#include "diagnostics/dpsl/public/dpsl_thread_context.h"
#include "diagnostics/dpsl/test_utils/common.h"
#include "wilco_dtc.grpc.pb.h" // NOLINT(build/include)
#include "wilco_dtc_supportd.grpc.pb.h" // NOLINT(build/include)
using testing::_;
using testing::ReturnRef;
using testing::StrictMock;
namespace diagnostics {
namespace {
constexpr DpslRpcServer::GrpcServerUri kGrpcServerUriInvalidValue =
static_cast<DpslRpcServer::GrpcServerUri>(
std::numeric_limits<
std::underlying_type<DpslRpcServer::GrpcServerUri>::type>::max());
class MockDpslRpcHandler : public DpslRpcHandler {
public:
void HandleMessageFromUi(
std::unique_ptr<grpc_api::HandleMessageFromUiRequest> request,
HandleMessageFromUiCallback callback) override {
DCHECK(request);
callback(std::make_unique<grpc_api::HandleMessageFromUiResponse>(
HandleMessageFromUiImpl(*request)));
}
void HandleEcNotification(
std::unique_ptr<grpc_api::HandleEcNotificationRequest> request,
HandleEcNotificationCallback callback) override {
DCHECK(request);
callback(std::make_unique<grpc_api::HandleEcNotificationResponse>(
HandleEcNotificationImpl(*request)));
}
void HandlePowerNotification(
std::unique_ptr<grpc_api::HandlePowerNotificationRequest> request,
HandlePowerNotificationCallback callback) override {
DCHECK(request);
callback(std::make_unique<grpc_api::HandlePowerNotificationResponse>(
HandlePowerNotificationImpl(*request)));
}
void HandleConfigurationDataChanged(
std::unique_ptr<grpc_api::HandleConfigurationDataChangedRequest> request,
HandleConfigurationDataChangedCallback callback) override {
DCHECK(request);
callback(std::make_unique<grpc_api::HandleConfigurationDataChangedResponse>(
HandleConfigurationDataChangedImpl(*request)));
}
void HandleBluetoothDataChanged(
std::unique_ptr<grpc_api::HandleBluetoothDataChangedRequest> request,
HandleBluetoothDataChangedCallback callback) override {
DCHECK(request);
callback(std::make_unique<grpc_api::HandleBluetoothDataChangedResponse>(
HandleBluetoothDataChangedImpl(*request)));
}
MOCK_METHOD(const grpc_api::HandleMessageFromUiResponse&,
HandleMessageFromUiImpl,
(const grpc_api::HandleMessageFromUiRequest& request));
MOCK_METHOD(const grpc_api::HandleEcNotificationResponse&,
HandleEcNotificationImpl,
(const grpc_api::HandleEcNotificationRequest& request));
MOCK_METHOD(const grpc_api::HandlePowerNotificationResponse&,
HandlePowerNotificationImpl,
(const grpc_api::HandlePowerNotificationRequest& request));
MOCK_METHOD(const grpc_api::HandleConfigurationDataChangedResponse&,
HandleConfigurationDataChangedImpl,
(const grpc_api::HandleConfigurationDataChangedRequest& request));
MOCK_METHOD(const grpc_api::HandleBluetoothDataChangedResponse&,
HandleBluetoothDataChangedImpl,
(const grpc_api::HandleBluetoothDataChangedRequest& request));
};
} // namespace
class DpslRpcServerImplBaseTest : public testing::Test {
public:
DpslRpcServerImplBaseTest() = default;
DpslRpcServerImplBaseTest(const DpslRpcServerImplBaseTest&) = delete;
DpslRpcServerImplBaseTest& operator=(const DpslRpcServerImplBaseTest&) =
delete;
~DpslRpcServerImplBaseTest() override {
DpslThreadContextImpl::CleanThreadCounterForTesting();
DpslGlobalContextImpl::CleanGlobalCounterForTesting();
}
void SetUp() override {
global_context_ = DpslGlobalContext::Create();
ASSERT_TRUE(global_context_);
thread_context_ = DpslThreadContext::Create(global_context_.get());
ASSERT_TRUE(thread_context_);
}
protected:
StrictMock<MockDpslRpcHandler> mock_handler_;
std::unique_ptr<DpslGlobalContext> global_context_;
std::unique_ptr<DpslThreadContext> thread_context_;
};
class DpslRpcServerImplBaseDeathTest : public DpslRpcServerImplBaseTest {
public:
DpslRpcServerImplBaseDeathTest() {
// Using EXPECT_DEATH, gtest creates child process, which re-executes the
// unit test binary just as it was originally invoked.
// https://github.com/google/googletest/blob/HEAD/googletest/docs/advanced.md#how-it-works
//
// Otherwise, EXPECT_DEATH statement will be called in forked child
// immediately. It means that at least
// DpslThreadContextImpl::BelongsToCurrentThread does fail. But somehow
// CHECK(sequence_checker_.CalledOnValidSequence()) does not.
::testing::FLAGS_gtest_death_test_style = "threadsafe";
}
DpslRpcServerImplBaseDeathTest(const DpslRpcServerImplBaseDeathTest&) =
delete;
DpslRpcServerImplBaseDeathTest& operator=(
const DpslRpcServerImplBaseDeathTest&) = delete;
};
TEST_F(DpslRpcServerImplBaseDeathTest, CreateWithNullThreadContext) {
EXPECT_DEATH(DpslRpcServer::Create(nullptr, &mock_handler_,
DpslRpcServer::GrpcServerUri::kVmVsock),
"Thread context is nullptr");
}
TEST_F(DpslRpcServerImplBaseDeathTest, CreateWithNullRpcHandler) {
EXPECT_DEATH(DpslRpcServer::Create(thread_context_.get(), nullptr,
DpslRpcServer::GrpcServerUri::kVmVsock),
"Rpc handler is nullptr");
}
TEST_F(DpslRpcServerImplBaseDeathTest, CreateWithInvalidServerUri) {
EXPECT_DEATH(DpslRpcServer::Create(thread_context_.get(), &mock_handler_,
kGrpcServerUriInvalidValue),
"Unexpected GrpcServerUri");
}
TEST_F(DpslRpcServerImplBaseDeathTest, MultiThreadInvalidThreadContext) {
TestDpslBackgroundThread background_thread(
"background", global_context_.get(), thread_context_.get());
EXPECT_DEATH(
DpslRpcServer::Create(background_thread.thread_context(), &mock_handler_,
kGrpcServerUriInvalidValue),
"Called from wrong thread");
}
// This is a parameterized test with the following parameters:
// * |grpc_server_uri| - gRPC server URI.
class DpslRpcServerImplTest
: public DpslRpcServerImplBaseTest,
public testing::WithParamInterface<DpslRpcServer::GrpcServerUri> {
public:
DpslRpcServerImplTest() = default;
DpslRpcServerImplTest(const DpslRpcServerImplTest&) = delete;
DpslRpcServerImplTest& operator=(const DpslRpcServerImplTest&) = delete;
DpslRpcServer::GrpcServerUri grpc_server_uri() const { return GetParam(); }
};
TEST_P(DpslRpcServerImplTest, CreateUsingVsock) {
EXPECT_TRUE(DpslRpcServer::Create(thread_context_.get(), &mock_handler_,
grpc_server_uri()));
}
TEST_P(DpslRpcServerImplTest, CreateUsingVsockTwiceOnAvailableAddress) {
// DpslRpcServer will be destroyed before the next call.
EXPECT_TRUE(DpslRpcServer::Create(thread_context_.get(), &mock_handler_,
grpc_server_uri()));
EXPECT_TRUE(DpslRpcServer::Create(thread_context_.get(), &mock_handler_,
grpc_server_uri()));
}
TEST_P(DpslRpcServerImplTest, CreateUsingVsockTwiceOnInUseAddress) {
auto dpsl_rpc_server = DpslRpcServer::Create(
thread_context_.get(), &mock_handler_, grpc_server_uri());
EXPECT_TRUE(dpsl_rpc_server);
EXPECT_FALSE(DpslRpcServer::Create(thread_context_.get(), &mock_handler_,
grpc_server_uri()));
}
INSTANTIATE_TEST_SUITE_P(
,
DpslRpcServerImplTest,
testing::Values(DpslRpcServer::GrpcServerUri::kVmVsock,
DpslRpcServer::GrpcServerUri::kUiMessageReceiverVmVsock));
// This is a parameterized test with the following parameters:
// * |grpc_server_uri| - gRPC server URI.
//
// Use UNIX socket for actual communication in tests between server(HOST) and
// client(HOST) since VSOCK can be used only for real HOST-VM communication.
//
// This class is still parameterized, because the behavior of DpslRpcServer
// depends on the URI.
class DpslRpcServerImplUnixSocketTest : public DpslRpcServerImplTest {
public:
DpslRpcServerImplUnixSocketTest() = default;
DpslRpcServerImplUnixSocketTest(const DpslRpcServerImplUnixSocketTest&) =
delete;
DpslRpcServerImplUnixSocketTest& operator=(
const DpslRpcServerImplUnixSocketTest&) = delete;
~DpslRpcServerImplUnixSocketTest() override = default;
void SetUp() override {
DpslRpcServerImplBaseTest::SetUp();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
dpsl_rpc_server_ = std::make_unique<DpslRpcServerImpl>(
&mock_handler_, grpc_server_uri(), grpc_server_uri_string());
ASSERT_TRUE(dpsl_rpc_server_->Init());
wilco_dtc_grpc_client_ =
std::make_unique<brillo::AsyncGrpcClient<grpc_api::WilcoDtc>>(
base::ThreadTaskRunnerHandle::Get(), grpc_server_uri_string());
}
void TearDown() override {
wilco_dtc_grpc_client_->ShutDown(base::Bind(
[](DpslThreadContext* thread_context) {
ASSERT_TRUE(thread_context);
thread_context->QuitEventLoop();
},
thread_context_.get()));
thread_context_->RunEventLoop();
DpslRpcServerImplTest::TearDown();
}
std::string grpc_server_uri_string() const {
return base::StringPrintf("unix:%s/test_wilco_dtc_socket",
temp_dir_.GetPath().value().c_str());
}
template <typename ClientRpcPtr,
typename ProtoRequest,
typename ProtoResponse>
void SendGrpcRequestAndCheckResponse(const ClientRpcPtr client_rpc_ptr,
const ProtoRequest& request,
const ProtoResponse& response) {
wilco_dtc_grpc_client_->CallRpc(
client_rpc_ptr, request,
base::Bind(
[](DpslThreadContext* thread_context,
const ProtoResponse& expected_response, grpc::Status status,
std::unique_ptr<ProtoResponse> response) {
ASSERT_TRUE(thread_context);
EXPECT_TRUE(status.ok());
ASSERT_TRUE(response);
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
*response, expected_response));
thread_context->QuitEventLoop();
},
thread_context_.get(), response));
thread_context_->RunEventLoop();
}
protected:
base::ScopedTempDir temp_dir_;
std::unique_ptr<brillo::AsyncGrpcClient<grpc_api::WilcoDtc>>
wilco_dtc_grpc_client_;
std::unique_ptr<DpslRpcServerImpl> dpsl_rpc_server_;
};
TEST_P(DpslRpcServerImplUnixSocketTest, HandleMessageFromUi) {
grpc_api::HandleMessageFromUiRequest request;
request.set_json_message("{'message': 'ping'}");
// Only RPC server which using kUiMessageReceiverVmVsock URI can receive
// HandleMessageFromUiRequest messages.
if (grpc_server_uri() !=
DpslRpcServer::GrpcServerUri::kUiMessageReceiverVmVsock) {
wilco_dtc_grpc_client_->CallRpc(
&grpc_api::WilcoDtc::Stub::AsyncHandleMessageFromUi, request,
base::Bind(
[](DpslThreadContext* thread_context, grpc::Status status,
std::unique_ptr<grpc_api::HandleMessageFromUiResponse>
response) {
ASSERT_TRUE(thread_context);
EXPECT_FALSE(status.ok());
thread_context->QuitEventLoop();
},
thread_context_.get()));
thread_context_->RunEventLoop();
return;
}
grpc_api::HandleMessageFromUiResponse response;
response.set_response_json_message("{'message': 'pong'}");
EXPECT_CALL(mock_handler_, HandleMessageFromUiImpl(ProtobufEquals(request)))
.WillOnce(ReturnRef(response));
SendGrpcRequestAndCheckResponse(
&grpc_api::WilcoDtc::Stub::AsyncHandleMessageFromUi, request, response);
}
TEST_P(DpslRpcServerImplUnixSocketTest, HandleEcNotification) {
grpc_api::HandleEcNotificationRequest request;
request.set_type(20);
request.set_payload("abcdef");
grpc_api::HandleEcNotificationResponse response;
EXPECT_CALL(mock_handler_, HandleEcNotificationImpl(ProtobufEquals(request)))
.WillOnce(ReturnRef(response));
SendGrpcRequestAndCheckResponse(
&grpc_api::WilcoDtc::Stub::AsyncHandleEcNotification, request, response);
}
TEST_P(DpslRpcServerImplUnixSocketTest, HandlePowerNotification) {
grpc_api::HandlePowerNotificationRequest request;
request.set_power_event(grpc_api::HandlePowerNotificationRequest::OS_SUSPEND);
grpc_api::HandlePowerNotificationResponse response;
EXPECT_CALL(mock_handler_,
HandlePowerNotificationImpl(ProtobufEquals(request)))
.WillOnce(ReturnRef(response));
SendGrpcRequestAndCheckResponse(
&grpc_api::WilcoDtc::Stub::AsyncHandlePowerNotification, request,
response);
}
TEST_P(DpslRpcServerImplUnixSocketTest, HandleConfigurationDataChanged) {
grpc_api::HandleConfigurationDataChangedRequest request;
grpc_api::HandleConfigurationDataChangedResponse response;
EXPECT_CALL(mock_handler_,
HandleConfigurationDataChangedImpl(ProtobufEquals(request)))
.WillOnce(ReturnRef(response));
SendGrpcRequestAndCheckResponse(
&grpc_api::WilcoDtc::Stub::AsyncHandleConfigurationDataChanged, request,
response);
}
TEST_P(DpslRpcServerImplUnixSocketTest, HandleBluetoothDataChanged) {
grpc_api::HandleBluetoothDataChangedRequest request;
grpc_api::HandleBluetoothDataChangedRequest::AdapterData* adapter_data =
request.add_adapters();
adapter_data->set_adapter_name("sarien");
adapter_data->set_adapter_mac_address("00:11:22:33:44:55");
adapter_data->set_carrier_status(
grpc_api::HandleBluetoothDataChangedRequest::AdapterData::STATUS_UP);
adapter_data->set_connected_devices_count(1);
grpc_api::HandleBluetoothDataChangedResponse response;
EXPECT_CALL(mock_handler_,
HandleBluetoothDataChangedImpl(ProtobufEquals(request)))
.WillOnce(ReturnRef(response));
SendGrpcRequestAndCheckResponse(
&grpc_api::WilcoDtc::Stub::AsyncHandleBluetoothDataChanged, request,
response);
}
INSTANTIATE_TEST_SUITE_P(
,
DpslRpcServerImplUnixSocketTest,
testing::Values(DpslRpcServer::GrpcServerUri::kVmVsock,
DpslRpcServer::GrpcServerUri::kUiMessageReceiverVmVsock));
} // namespace diagnostics