blob: eae23ac4d77af8436eee0652528636d14ae0f8a3 [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 "ipcz/node.h"
#include <utility>
#include "ipcz/driver_memory.h"
#include "ipcz/driver_transport.h"
#include "ipcz/ipcz.h"
#include "ipcz/link_side.h"
#include "ipcz/node_link.h"
#include "ipcz/node_link_memory.h"
#include "ipcz/node_name.h"
#include "reference_drivers/sync_reference_driver.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "util/ref_counted.h"
namespace ipcz {
namespace {
const IpczDriver& kTestDriver = reference_drivers::kSyncReferenceDriver;
constexpr NodeName kNodeAName(0, 1);
constexpr NodeName kNodeBName(1, 2);
constexpr NodeName kNodeCName(3, 5);
class NodeTest : public testing::Test {
public:
void SetUp() override {
ConnectBrokerToNode(node_a_, kNodeAName);
ConnectBrokerToNode(node_b_, kNodeBName);
}
void TearDown() override {
node_b_->Close();
node_a_->Close();
broker_->Close();
}
Node& broker() { return *broker_; }
NodeName broker_name() { return broker_->GetAssignedName(); }
Node& node_a() { return *node_a_; }
Node& node_b() { return *node_b_; }
private:
void ConnectBrokerToNode(Ref<Node> node, const NodeName& name) {
auto transports = DriverTransport::CreatePair(kTestDriver);
DriverMemoryWithMapping buffer =
NodeLinkMemory::AllocateMemory(kTestDriver);
const NodeName broker_name = broker_->GetAssignedName();
auto broker_link = NodeLink::CreateInactive(
broker_, LinkSide::kA, broker_name, name, Node::Type::kNormal, 0,
transports.first,
NodeLinkMemory::Create(broker_, std::move(buffer.mapping)));
auto node_link = NodeLink::CreateInactive(
node, LinkSide::kB, name, broker_name, Node::Type::kBroker, 0,
transports.second, NodeLinkMemory::Create(node, buffer.memory.Map()));
node->SetAssignedName(name);
broker_->AddLink(name, broker_link);
node->AddLink(broker_name, node_link);
node->SetBrokerLink(node_link);
broker_link->Activate();
node_link->Activate();
}
const Ref<Node> broker_{MakeRefCounted<Node>(Node::Type::kBroker,
kTestDriver,
IPCZ_INVALID_DRIVER_HANDLE)};
const Ref<Node> node_a_{MakeRefCounted<Node>(Node::Type::kNormal,
kTestDriver,
IPCZ_INVALID_DRIVER_HANDLE)};
const Ref<Node> node_b_{MakeRefCounted<Node>(Node::Type::kNormal,
kTestDriver,
IPCZ_INVALID_DRIVER_HANDLE)};
};
TEST_F(NodeTest, EstablishExistingLinks) {
// When the requested node is already known, EstablishLink() responds
// immediately with the existing NodeLink.
bool called = false;
auto expect_link = [&called](Ref<NodeLink>& expected) {
return [expected = expected.get(), &called](NodeLink* link) {
EXPECT_EQ(expected, link);
called = true;
};
};
Ref<NodeLink> broker_to_a = broker().GetLink(kNodeAName);
EXPECT_TRUE(broker_to_a);
broker().EstablishLink(kNodeAName, expect_link(broker_to_a));
EXPECT_TRUE(called);
called = false;
Ref<NodeLink> broker_to_b = broker().GetLink(kNodeBName);
EXPECT_TRUE(broker_to_b);
broker().EstablishLink(kNodeBName, expect_link(broker_to_b));
EXPECT_TRUE(called);
called = false;
Ref<NodeLink> a_to_broker = node_a().GetLink(broker_name());
EXPECT_TRUE(a_to_broker);
node_a().EstablishLink(broker_name(), expect_link(a_to_broker));
EXPECT_TRUE(called);
called = false;
Ref<NodeLink> b_to_broker = node_b().GetLink(broker_name());
EXPECT_TRUE(b_to_broker);
node_b().EstablishLink(broker_name(), expect_link(b_to_broker));
EXPECT_TRUE(called);
called = false;
}
TEST_F(NodeTest, EstablishNewLinks) {
// When the requested node is not yet known to the caller but is known to
// their broker, EstablishLink() coordinates with the broker to get the
// two nodes introduced to each other.
NodeLink* established_link = nullptr;
EXPECT_FALSE(node_a().GetLink(kNodeBName));
EXPECT_FALSE(node_b().GetLink(kNodeAName));
node_a().EstablishLink(kNodeBName, [&established_link](NodeLink* link) {
established_link = link;
});
EXPECT_TRUE(established_link);
Ref<NodeLink> a_to_b = node_a().GetLink(kNodeBName);
Ref<NodeLink> b_to_a = node_b().GetLink(kNodeAName);
EXPECT_TRUE(a_to_b);
EXPECT_TRUE(b_to_a);
EXPECT_EQ(a_to_b.get(), established_link);
// A redundant EstablishLink() changes nothing, even from the other side.
node_a().EstablishLink(
kNodeBName, [&a_to_b](NodeLink* link) { EXPECT_EQ(a_to_b.get(), link); });
node_b().EstablishLink(
kNodeAName, [&b_to_a](NodeLink* link) { EXPECT_EQ(b_to_a.get(), link); });
EXPECT_EQ(a_to_b, node_a().GetLink(kNodeBName));
EXPECT_EQ(b_to_a, node_b().GetLink(kNodeAName));
// Verify that the new links are managing a common buffer pool.
constexpr uint64_t kMagic = 0x1123581321345589;
const Fragment a_fragment = a_to_b->memory().AllocateFragment(8);
EXPECT_TRUE(a_fragment.is_addressable());
*static_cast<uint64_t*>(a_fragment.address()) = kMagic;
const Fragment b_fragment =
b_to_a->memory().GetFragment(a_fragment.descriptor());
EXPECT_TRUE(b_fragment.is_addressable());
EXPECT_EQ(kMagic, *static_cast<uint64_t*>(b_fragment.address()));
*static_cast<uint64_t*>(b_fragment.address()) = 0;
EXPECT_EQ(0u, *static_cast<uint64_t*>(a_fragment.address()));
}
TEST_F(NodeTest, EstablishLinkFailureFromNonBroker) {
// If the named node is unknown to the broker, a link can't be established by
// a non-broker.
bool failed = false;
EXPECT_FALSE(broker().GetLink(kNodeCName));
EXPECT_FALSE(node_a().GetLink(kNodeCName));
node_a().EstablishLink(kNodeCName, [&](NodeLink* link) {
EXPECT_FALSE(link);
failed = true;
});
EXPECT_TRUE(failed);
}
TEST_F(NodeTest, EstablishLinkFailureFromBroker) {
// New links can't be automatically established by the broker.
bool failed = false;
EXPECT_FALSE(broker().GetLink(kNodeCName));
broker().EstablishLink(kNodeCName, [&](NodeLink* link) {
EXPECT_FALSE(link);
failed = true;
});
EXPECT_TRUE(failed);
}
TEST_F(NodeTest, EstablishLinkFailureWithoutBrokerLink) {
// A node with no broker link can't be introduced to anyone.
bool failed = false;
const Ref<Node> node_c = MakeRefCounted<Node>(
Node::Type::kNormal, kTestDriver, IPCZ_INVALID_DRIVER_HANDLE);
EXPECT_TRUE(broker().GetLink(kNodeAName));
node_c->EstablishLink(kNodeAName, [&](NodeLink* link) {
EXPECT_FALSE(link);
failed = true;
});
EXPECT_TRUE(failed);
}
} // namespace
} // namespace ipcz