blob: b75e54dd650efe716f140e5a0bef3ae506f19e9e [file] [log] [blame]
// Copyright 2021 The Chromium 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 "remoting/host/mojo_ipc/mojo_ipc_server.h"
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/no_destructor.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/platform/named_platform_channel.h"
#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
#include "mojo/public/cpp/system/isolated_connection.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "remoting/host/mojo_ipc/mojo_ipc_test_util.h"
#include "remoting/host/mojom/testing.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace remoting {
namespace {
using testing::_;
using testing::Return;
using EchoStringHandler = base::RepeatingCallback<void(
const std::string& input,
test::mojom::Echo::EchoStringCallback callback)>;
void SendEchoAndVerifyResponse(mojo::Remote<test::mojom::Echo>& echo_remote) {
base::RunLoop echo_response_run_loop;
base::MockCallback<test::mojom::Echo::EchoStringCallback> callback;
EXPECT_CALL(callback, Run("test string"))
.WillOnce(
base::test::RunOnceClosure(echo_response_run_loop.QuitClosure()));
echo_remote->EchoString("test string", callback.Get());
echo_response_run_loop.Run();
}
} // namespace
class MojoIpcServerTest : public testing::Test, public test::mojom::Echo {
public:
MojoIpcServerTest();
~MojoIpcServerTest() override;
void SetUp() override;
protected:
mojo::PlatformChannelEndpoint ConnectToTestServer();
mojo::Remote<test::mojom::Echo> ConnectAndCreateEchoRemote(
mojo::IsolatedConnection& client_connection);
void WaitForInvitationSent();
std::unique_ptr<MojoIpcServer<test::mojom::Echo>> ipc_server_;
mojo::ReceiverId last_echo_string_receiver_id_ = 0u;
// If this is set, EchoString() will run this callback instead of responding
// to the callback automatically.
EchoStringHandler echo_string_handler_;
private:
void EchoString(const std::string& input,
EchoStringCallback callback) override;
void OnInvitationSent();
mojo::NamedPlatformChannel::ServerName test_server_name_;
base::test::TaskEnvironment task_environment_;
// Run loops that wait for MojoIpcServerBase::ObserverForTesting methods to
// be called.
std::unique_ptr<base::RunLoop> on_invitation_sent_run_loop_;
};
MojoIpcServerTest::MojoIpcServerTest() {
test_server_name_ = test::GenerateRandomServerName();
ipc_server_ = std::make_unique<MojoIpcServer<test::mojom::Echo>>(
test_server_name_, this);
ipc_server_->set_on_invitation_sent_callback_for_testing(base::BindRepeating(
&MojoIpcServerTest::OnInvitationSent, base::Unretained(this)));
on_invitation_sent_run_loop_ = std::make_unique<base::RunLoop>();
}
MojoIpcServerTest::~MojoIpcServerTest() = default;
void MojoIpcServerTest::SetUp() {
ipc_server_->StartServer();
WaitForInvitationSent();
}
mojo::PlatformChannelEndpoint MojoIpcServerTest::ConnectToTestServer() {
return mojo::NamedPlatformChannel::ConnectToServer(test_server_name_);
}
mojo::Remote<test::mojom::Echo> MojoIpcServerTest::ConnectAndCreateEchoRemote(
mojo::IsolatedConnection& client_connection) {
return mojo::Remote<test::mojom::Echo>(mojo::PendingRemote<test::mojom::Echo>(
client_connection.Connect(ConnectToTestServer()), /* version= */ 0));
}
void MojoIpcServerTest::WaitForInvitationSent() {
on_invitation_sent_run_loop_->Run();
on_invitation_sent_run_loop_ = std::make_unique<base::RunLoop>();
}
void MojoIpcServerTest::EchoString(const std::string& input,
EchoStringCallback callback) {
if (echo_string_handler_) {
echo_string_handler_.Run(input, std::move(callback));
return;
}
std::move(callback).Run(input);
last_echo_string_receiver_id_ = ipc_server_->current_receiver();
}
void MojoIpcServerTest::OnInvitationSent() {
on_invitation_sent_run_loop_->Quit();
}
TEST_F(MojoIpcServerTest, SendEcho) {
mojo::IsolatedConnection client_connection;
auto echo_remote = ConnectAndCreateEchoRemote(client_connection);
SendEchoAndVerifyResponse(echo_remote);
}
TEST_F(MojoIpcServerTest, DeleteMojoServer_NoLingeringInvitations) {
ipc_server_.reset();
base::RunLoop().RunUntilIdle();
// For posix, the socket doesn't seem to be closed immediately after the
// isolated connection is deleted, so we wait for 1s to make sure the socket
// is really closed.
base::PlatformThread::Sleep(base::Seconds(1));
auto endpoint = ConnectToTestServer();
ASSERT_FALSE(endpoint.is_valid());
}
TEST_F(MojoIpcServerTest, DisconnectHandler) {
base::RunLoop disconnect_run_loop;
base::MockCallback<base::RepeatingClosure> disconnect_handler;
EXPECT_CALL(disconnect_handler, Run()).WillOnce([&]() {
ASSERT_EQ(last_echo_string_receiver_id_, ipc_server_->current_receiver());
disconnect_run_loop.Quit();
});
ipc_server_->set_disconnect_handler(disconnect_handler.Get());
auto client_connection = std::make_unique<mojo::IsolatedConnection>();
auto echo_remote = ConnectAndCreateEchoRemote(*client_connection);
// Must send some IPC to be considered connected.
SendEchoAndVerifyResponse(echo_remote);
echo_remote.reset();
client_connection.reset();
disconnect_run_loop.Run();
}
TEST_F(MojoIpcServerTest, DeleteMojoServer_RemoteDisconnected) {
mojo::IsolatedConnection client_connection;
auto echo_remote = ConnectAndCreateEchoRemote(client_connection);
SendEchoAndVerifyResponse(echo_remote);
base::RunLoop disconnect_run_loop;
echo_remote.set_disconnect_handler(disconnect_run_loop.QuitClosure());
ipc_server_.reset();
disconnect_run_loop.Run();
}
TEST_F(MojoIpcServerTest, StopServer_RemoteDisconnected) {
mojo::IsolatedConnection client_connection;
auto echo_remote = ConnectAndCreateEchoRemote(client_connection);
SendEchoAndVerifyResponse(echo_remote);
base::RunLoop disconnect_run_loop;
echo_remote.set_disconnect_handler(disconnect_run_loop.QuitClosure());
ipc_server_->StopServer();
disconnect_run_loop.Run();
}
TEST_F(MojoIpcServerTest, CloseReceiver_RemoteDisconnected) {
mojo::IsolatedConnection client_connection;
auto echo_remote = ConnectAndCreateEchoRemote(client_connection);
SendEchoAndVerifyResponse(echo_remote);
base::RunLoop disconnect_run_loop;
echo_remote.set_disconnect_handler(disconnect_run_loop.QuitClosure());
ipc_server_->Close(last_echo_string_receiver_id_);
disconnect_run_loop.Run();
}
TEST_F(MojoIpcServerTest, ParallelIpcs) {
base::RunLoop echo_response_run_loop;
base::MockCallback<EchoStringCallback> echo_mock_callback;
EXPECT_CALL(echo_mock_callback, Run(_))
.WillOnce(Return())
.WillOnce(
base::test::RunOnceClosure(echo_response_run_loop.QuitClosure()));
base::MockCallback<EchoStringHandler> mock_echo_string_handler;
echo_string_handler_ = mock_echo_string_handler.Get();
std::string first_input;
EchoStringCallback first_callback;
EXPECT_CALL(mock_echo_string_handler, Run(_, _))
.WillOnce([&](const std::string& input, EchoStringCallback callback) {
first_input = input;
first_callback = std::move(callback);
})
.WillOnce([&](const std::string& input, EchoStringCallback callback) {
std::move(first_callback).Run(first_input);
std::move(callback).Run(input);
});
mojo::IsolatedConnection client_connection_1;
auto echo_remote_1 = ConnectAndCreateEchoRemote(client_connection_1);
echo_remote_1->EchoString("test string 1", echo_mock_callback.Get());
WaitForInvitationSent();
mojo::IsolatedConnection client_connection_2;
auto echo_remote_2 = ConnectAndCreateEchoRemote(client_connection_2);
echo_remote_2->EchoString("test string 2", echo_mock_callback.Get());
echo_response_run_loop.Run();
}
} // namespace remoting