chromium/ipc/ipc_sync_channel_unittest.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ipc/ipc_sync_channel.h"

#include <stddef.h>

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/message_loop/message_pump_type.h"
#include "base/process/process_handle.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/task_environment.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "ipc/ipc_listener.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_sender.h"
#include "ipc/ipc_sync_message_filter.h"
#include "ipc/ipc_sync_message_unittest.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "testing/gtest/include/gtest/gtest.h"

WaitableEvent;

namespace IPC {
namespace {

// Base class for a "process" with listener and IPC threads.
class Worker : public Listener, public Sender {};


// Starts the test with the given workers.  This function deletes the workers
// when it's done.
void RunTest(std::vector<Worker*> workers) {}

class IPCSyncChannelTest : public testing::Test {};

//------------------------------------------------------------------------------

class SimpleServer : public Worker {};

class SimpleClient : public Worker {};

void Simple() {}

#if BUILDFLAG(IS_ANDROID)
#define MAYBE_Simple
#else
#define MAYBE_Simple
#endif
// Tests basic synchronous call
TEST_F(IPCSyncChannelTest, MAYBE_Simple) {}

//------------------------------------------------------------------------------

// Worker classes which override how the sync channel is created to use the
// two-step initialization (calling the lightweight constructor and then
// ChannelProxy::Init separately) process.
class TwoStepServer : public Worker {};

class TwoStepClient : public Worker {};

void TwoStep(bool create_server_pipe_now, bool create_client_pipe_now) {}

// Tests basic two-step initialization, where you call the lightweight
// constructor then Init.
TEST_F(IPCSyncChannelTest, TwoStepInitialization) {}

//------------------------------------------------------------------------------

class DelayClient : public Worker {};

void DelayReply() {}

// Tests that asynchronous replies work
TEST_F(IPCSyncChannelTest, DelayReply) {}

//------------------------------------------------------------------------------

class NoHangServer : public Worker {};

class NoHangClient : public Worker {};

void NoHang() {}

// Tests that caller doesn't hang if receiver dies
TEST_F(IPCSyncChannelTest, NoHang) {}

//------------------------------------------------------------------------------

class UnblockServer : public Worker {};

class UnblockClient : public Worker {};

void Unblock(bool delete_during_send) {}

// Tests that the caller unblocks to answer a sync message from the receiver.
TEST_F(IPCSyncChannelTest, Unblock) {}

//------------------------------------------------------------------------------

#if BUILDFLAG(IS_ANDROID)
#define MAYBE_ChannelDeleteDuringSend
#else
#define MAYBE_ChannelDeleteDuringSend
#endif
// Tests that the the SyncChannel object can be deleted during a Send.
TEST_F(IPCSyncChannelTest, MAYBE_ChannelDeleteDuringSend) {}

//------------------------------------------------------------------------------

class RecursiveServer : public Worker {};

class RecursiveClient : public Worker {};

void Recursive() {}

// Tests a server calling Send while another Send is pending.
TEST_F(IPCSyncChannelTest, Recursive) {}

//------------------------------------------------------------------------------

void RecursiveNoHang() {}

// Tests that if a caller makes a sync call during an existing sync call and
// the receiver dies, neither of the Send() calls hang.
TEST_F(IPCSyncChannelTest, RecursiveNoHang) {}

//------------------------------------------------------------------------------

class MultipleServer1 : public Worker {};

class MultipleClient1 : public Worker {};

class MultipleServer2 : public Worker {};

class MultipleClient2 : public Worker {};

void Multiple() {}

// Tests that multiple SyncObjects on the same listener thread can unblock each
// other.
TEST_F(IPCSyncChannelTest, Multiple) {}

//------------------------------------------------------------------------------

// This class provides server side functionality to test the case where
// multiple sync channels are in use on the same thread on the client.
class QueuedReplyServer : public Worker {};

// The QueuedReplyClient class provides functionality to test the case where
// multiple sync channels are in use on the same thread.
class QueuedReplyClient : public Worker {};

void QueuedReply() {}

// While a blocking send is in progress, the listener thread might answer other
// synchronous messages.  This tests that if during the response to another
// message the reply to the original messages comes, it is queued up correctly
// and the original Send is unblocked later.
TEST_F(IPCSyncChannelTest, QueuedReply) {}

//------------------------------------------------------------------------------

class TestSyncMessageFilter : public SyncMessageFilter {};

class SyncMessageFilterServer : public Worker {};

// This class provides functionality to test the case that a Send on the sync
// channel does not crash after the channel has been closed.
class ServerSendAfterClose : public Worker {};

// Tests basic synchronous call
TEST_F(IPCSyncChannelTest, SyncMessageFilter) {}

// Test the case when the channel is closed and a Send is attempted after that.
TEST_F(IPCSyncChannelTest, SendAfterClose) {}

//------------------------------------------------------------------------------

class RestrictedDispatchServer : public Worker {};

class NonRestrictedDispatchServer : public Worker {};

class RestrictedDispatchClient : public Worker {};

TEST_F(IPCSyncChannelTest, RestrictedDispatch) {}

//------------------------------------------------------------------------------

// This test case inspired by crbug.com/108491
// We create two servers that use the same ListenerThread but have
// SetRestrictDispatchToSameChannel set to true.
// We create clients, then use some specific WaitableEvent wait/signalling to
// ensure that messages get dispatched in a way that causes a deadlock due to
// a nested dispatch and an eligible message in a higher-level dispatch's
// delayed_queue. Specifically, we start with client1 about so send an
// unblocking message to server1, while the shared listener thread for the
// servers server1 and server2 is about to send a non-unblocking message to
// client1. At the same time, client2 will be about to send an unblocking
// message to server2. Server1 will handle the client1->server1 message by
// telling server2 to send a non-unblocking message to client2.
// What should happen is that the send to server2 should find the pending,
// same-context client2->server2 message to dispatch, causing client2 to
// unblock then handle the server2->client2 message, so that the shared
// servers' listener thread can then respond to the client1->server1 message.
// Then client1 can handle the non-unblocking server1->client1 message.
// The old code would end up in a state where the server2->client2 message is
// sent, but the client2->server2 message (which is eligible for dispatch, and
// which is what client2 is waiting for) is stashed in a local delayed_queue
// that has server1's channel context, causing a deadlock.
// WaitableEvents in the events array are used to:
//   event 0: indicate to client1 that server listener is in OnDoServerTask
//   event 1: indicate to client1 that client2 listener is in OnDoClient2Task
//   event 2: indicate to server1 that client2 listener is in OnDoClient2Task
//   event 3: indicate to client2 that server listener is in OnDoServerTask

class RestrictedDispatchDeadlockServer : public Worker {};

class RestrictedDispatchDeadlockClient2 : public Worker {};

class RestrictedDispatchDeadlockClient1 : public Worker {};

TEST_F(IPCSyncChannelTest, RestrictedDispatchDeadlock) {}

//------------------------------------------------------------------------------

// This test case inspired by crbug.com/120530
// We create 4 workers that pipe to each other W1->W2->W3->W4->W1 then we send a
// message that recurses through 3, 4 or 5 steps to make sure, say, W1 can
// re-enter when called from W4 while it's sending a message to W2.
// The first worker drives the whole test so it must be treated specially.

class RestrictedDispatchPipeWorker : public Worker {};

#if BUILDFLAG(IS_ANDROID)
#define MAYBE_RestrictedDispatch4WayDeadlock
#else
#define MAYBE_RestrictedDispatch4WayDeadlock
#endif
TEST_F(IPCSyncChannelTest, MAYBE_RestrictedDispatch4WayDeadlock) {}

//------------------------------------------------------------------------------

// This test case inspired by crbug.com/122443
// We want to make sure a reply message with the unblock flag set correctly
// behaves as a reply, not a regular message.
// We have 3 workers. Server1 will send a message to Server2 (which will block),
// during which it will dispatch a message comming from Client, at which point
// it will send another message to Server2. While sending that second message it
// will receive a reply from Server1 with the unblock flag.

class ReentrantReplyServer1 : public Worker {};

class ReentrantReplyServer2 : public Worker {};

class ReentrantReplyClient : public Worker {};

TEST_F(IPCSyncChannelTest, ReentrantReply) {}

}  // namespace
}  // namespace IPC