blob: 2d41f4f2b31ce5bad335fcc8e014060fad9059f6 [file] [log] [blame]
// Copyright 2022 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 <string>
#include <string_view>
#include <utility>
#include "ipcz/ipcz.h"
#include "test/multinode_test.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ipcz {
namespace {
using RemotePortalTestNode = test::TestNode;
using RemotePortalTest = test::MultinodeTest<RemotePortalTestNode>;
static constexpr std::string_view kTestMessage1 = "hello world";
static constexpr std::string_view kTestMessage2 = "hola mundo";
MULTINODE_TEST_NODE(RemotePortalTestNode, BasicConnectionClient) {
IpczHandle b = ConnectToBroker();
EXPECT_EQ(IPCZ_RESULT_OK, Put(b, kTestMessage1));
std::string message;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, &message));
EXPECT_EQ(kTestMessage2, message);
Close(b);
}
TEST_P(RemotePortalTest, BasicConnection) {
IpczHandle c = SpawnTestNode<BasicConnectionClient>();
std::string message;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(c, &message));
EXPECT_EQ(kTestMessage1, message);
EXPECT_EQ(IPCZ_RESULT_OK, Put(c, kTestMessage2));
Close(c);
}
MULTINODE_TEST_NODE(RemotePortalTestNode, PortalTransferClient) {
IpczHandle b = ConnectToBroker();
IpczHandle p = IPCZ_INVALID_HANDLE;
std::string message;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, &message, {&p, 1}));
EXPECT_EQ(kTestMessage1, message);
EXPECT_NE(IPCZ_INVALID_HANDLE, p);
VerifyEndToEnd(p);
CloseAll({p, b});
}
TEST_P(RemotePortalTest, PortalTransfer) {
IpczHandle c = SpawnTestNode<PortalTransferClient>();
auto [q, p] = OpenPortals();
EXPECT_EQ(IPCZ_RESULT_OK, Put(c, kTestMessage1, {&p, 1}));
VerifyEndToEnd(q);
CloseAll({q, c});
}
constexpr size_t kMultipleHopsNumIterations = 100;
MULTINODE_TEST_NODE(RemotePortalTestNode, MultipleHopsClient1) {
IpczHandle b = ConnectToBroker();
auto [q, p] = OpenPortals();
EXPECT_EQ(IPCZ_RESULT_OK, Put(b, "", {&p, 1}));
for (size_t i = 0; i < kMultipleHopsNumIterations; ++i) {
EXPECT_EQ(IPCZ_RESULT_OK, Put(q, kTestMessage2));
}
for (size_t i = 0; i < kMultipleHopsNumIterations; ++i) {
std::string message;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(q, &message));
EXPECT_EQ(kTestMessage1, message);
}
PingPong(b);
CloseAll({q, b});
}
MULTINODE_TEST_NODE(RemotePortalTestNode, MultipleHopsClient2) {
IpczHandle b = ConnectToBroker();
IpczHandle p;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, nullptr, {&p, 1}));
for (size_t i = 0; i < kMultipleHopsNumIterations; ++i) {
EXPECT_EQ(IPCZ_RESULT_OK, Put(p, kTestMessage1));
}
for (size_t i = 0; i < kMultipleHopsNumIterations; ++i) {
std::string message;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(p, &message));
EXPECT_EQ(kTestMessage2, message);
}
PingPong(b);
CloseAll({p, b});
}
TEST_P(RemotePortalTest, MultipleHops) {
IpczHandle c1 = SpawnTestNode<MultipleHopsClient1>();
IpczHandle c2 = SpawnTestNode<MultipleHopsClient2>();
IpczHandle p;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(c1, nullptr, {&p, 1}));
EXPECT_EQ(IPCZ_RESULT_OK, Put(c2, "", {&p, 1}));
WaitForPingAndReply(c1);
WaitForPingAndReply(c2);
CloseAll({c1, c2});
}
constexpr size_t kTransferBackAndForthNumIterations = 1;
MULTINODE_TEST_NODE(RemotePortalTestNode, TransferBackAndForthClient) {
IpczHandle b = ConnectToBroker();
for (size_t i = 0; i < kTransferBackAndForthNumIterations; ++i) {
IpczHandle p;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, nullptr, {&p, 1}));
EXPECT_EQ(IPCZ_RESULT_OK, Put(b, "", {&p, 1}));
}
EXPECT_EQ(IPCZ_RESULT_OK, WaitForConditionFlags(b, IPCZ_TRAP_PEER_CLOSED));
Close(b);
}
TEST_P(RemotePortalTest, TransferBackAndForth) {
IpczHandle c = SpawnTestNode<TransferBackAndForthClient>();
constexpr std::string_view kMessage = "hihihihi";
auto [q, p] = OpenPortals();
std::string message;
for (size_t i = 0; i < kTransferBackAndForthNumIterations; ++i) {
EXPECT_EQ(IPCZ_RESULT_OK, Put(q, kMessage));
EXPECT_EQ(IPCZ_RESULT_OK, Put(c, "", {&p, 1}));
p = IPCZ_INVALID_HANDLE;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(c, nullptr, {&p, 1}));
EXPECT_NE(IPCZ_INVALID_HANDLE, p);
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(p, &message));
EXPECT_EQ(kMessage, message);
}
VerifyEndToEndLocal(q, p);
CloseAll({q, p, c});
}
MULTINODE_TEST_NODE(RemotePortalTestNode, DisconnectThroughProxyClient1) {
IpczHandle b = ConnectToBroker();
IpczHandle q;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, nullptr, {&q, 1}));
// Should eventually be observed by virtue of the forced disconnection of
// client 3 in the main test body.
EXPECT_EQ(IPCZ_RESULT_OK, WaitForConditionFlags(q, IPCZ_TRAP_PEER_CLOSED));
CloseAll({q, b});
}
MULTINODE_TEST_NODE(RemotePortalTestNode, DisconnectThroughProxyClient2) {
IpczHandle b = ConnectToBroker();
// Receive a portal p from the broker and bounce it back.
IpczHandle p;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, nullptr, {&p, 1}));
EXPECT_EQ(IPCZ_RESULT_OK, Put(b, "", {&p, 1}));
EXPECT_EQ(IPCZ_RESULT_OK, WaitForConditionFlags(b, IPCZ_TRAP_PEER_CLOSED));
Close(b);
}
MULTINODE_TEST_NODE(RemotePortalTestNode, DisconnectThroughProxyClient3) {
IpczHandle b = ConnectToBroker();
// Receive a portal p from the broker and then immediately terminate this
// node.
IpczHandle p;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, nullptr, {&p, 1}));
// Forcibly shut this node down, severing all connections to other nodes. We
// do this *before* closing `p` to ensure that we are't just exercising normal
// portal closure, which other tests already cover. From the perspective of
// other nodes, we are effectively simulating a crash of this node.
CloseThisNode();
// It's still necessary to explicitly close local portals after the node
// itself has been shut down. Otherwise they'd leak.
CloseAll({p, b});
}
TEST_P(RemotePortalTest, DisconnectThroughProxy) {
// Exercises node disconnection. Namely if portals on nodes 1 and 3 are
// connected via proxy on node 2, and node 3 disappears, node 1's portal
// should observe peer closure.
IpczHandle c1, c3;
auto c1_control = SpawnTestNode<DisconnectThroughProxyClient1>({&c1, 1});
IpczHandle c2 = SpawnTestNode<DisconnectThroughProxyClient2>();
auto c3_control = SpawnTestNode<DisconnectThroughProxyClient3>({&c3, 1});
auto [q, p] = OpenPortals();
// We send q to client 1, and p to client 2.
EXPECT_EQ(IPCZ_RESULT_OK, Put(c1, "", {&q, 1}));
EXPECT_EQ(IPCZ_RESULT_OK, Put(c2, "", {&p, 1}));
// Client 2 forwards p back to us, and we forward it now to client 3. This
// process ensures that client 2 will for some time serve as a proxy between
// client 1 and client 3.
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(c2, nullptr, {&p, 1}));
EXPECT_EQ(IPCZ_RESULT_OK, Put(c3, "", {&p, 1}));
// Client 3 will terminate on its own. Though not determinstic, this will
// race with proxy reduction such that client 2 may still be proxying for the
// q-p portal pair when client 3 (who owns p) goes away.
EXPECT_TRUE(c3_control->WaitForShutdown());
// Client 1 waits on q observing peer closure before terminating. This will
// block until that happens.
EXPECT_TRUE(c1_control->WaitForShutdown());
CloseAll({c1, c2, c3});
}
INSTANTIATE_MULTINODE_TEST_SUITE_P(RemotePortalTest);
} // namespace
} // namespace ipcz