blob: 3790412afc479e4887fbd72d7d2acc700c0cf07b [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/router_link.h"
#include <tuple>
#include "ipcz/driver_transport.h"
#include "ipcz/ipcz.h"
#include "ipcz/link_side.h"
#include "ipcz/link_type.h"
#include "ipcz/local_router_link.h"
#include "ipcz/node.h"
#include "ipcz/node_link.h"
#include "ipcz/node_name.h"
#include "ipcz/remote_router_link.h"
#include "ipcz/router.h"
#include "ipcz/router_link_state.h"
#include "reference_drivers/sync_reference_driver.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "util/ref_counted.h"
namespace ipcz {
namespace {
enum class RouterLinkTestMode {
kLocal,
kRemote,
};
const IpczDriver& kTestDriver = reference_drivers::kSyncReferenceDriver;
constexpr NodeName kTestBrokerName(1, 2);
constexpr NodeName kTestNonBrokerName(2, 3);
constexpr NodeName kTestPeer1Name(3, 4);
constexpr NodeName kTestPeer2Name(4, 5);
class RouterLinkTest : public testing::Test,
public testing::WithParamInterface<RouterLinkTestMode> {
public:
void SetUp() override {
switch (GetParam()) {
case RouterLinkTestMode::kLocal:
std::tie(a_link_, b_link_) =
LocalRouterLink::ConnectRouters(LinkType::kCentral, {a_, b_});
break;
case RouterLinkTestMode::kRemote: {
auto transports = DriverTransport::CreatePair(kTestDriver);
auto alloc = NodeLinkMemory::Allocate(broker_);
broker_node_link_ = NodeLink::Create(
broker_, LinkSide::kA, kTestBrokerName, kTestNonBrokerName,
Node::Type::kNormal, 0, transports.first,
std::move(alloc.node_link_memory));
non_broker_node_link_ = NodeLink::Create(
non_broker_, LinkSide::kB, kTestNonBrokerName, kTestBrokerName,
Node::Type::kBroker, 0, transports.second,
NodeLinkMemory::Adopt(non_broker_,
std::move(alloc.primary_buffer_memory)));
broker_->AddLink(kTestNonBrokerName, broker_node_link_);
non_broker_->AddLink(kTestBrokerName, non_broker_node_link_);
auto fragment = broker_node_link_->memory().AllocateFragment(
sizeof(RouterLinkState));
auto link_state = FragmentRef<RouterLinkState>(
RefCountedFragment::kAdoptExistingRef,
WrapRefCounted(&broker_node_link_->memory()), fragment);
RouterLinkState::Initialize(link_state.get());
a_link_ = broker_node_link_->AddRemoteRouterLink(
SublinkId{0}, link_state, LinkType::kCentral, LinkSide::kA, a_);
b_link_ = non_broker_node_link_->AddRemoteRouterLink(
SublinkId{0}, link_state, LinkType::kCentral, LinkSide::kB, b_);
a_->SetOutwardLink(a_link_);
b_->SetOutwardLink(b_link_);
broker_node_link_->transport()->Activate();
non_broker_node_link_->transport()->Activate();
break;
}
}
ASSERT_EQ(a_link_->GetLinkState(), b_link_->GetLinkState());
link_state_ = a_link_->GetLinkState();
}
void TearDown() override {
a_->CloseRoute();
b_->CloseRoute();
broker_->Close();
non_broker_->Close();
}
Router& a() { return *a_; }
Router& b() { return *b_; }
RouterLink& a_link() { return *a_link_; }
RouterLink& b_link() { return *b_link_; }
RouterLinkState& link_state() { return *link_state_; }
RouterLinkState::Status link_status() { return link_state_->status; }
private:
const Ref<Node> broker_{MakeRefCounted<Node>(Node::Type::kBroker,
kTestDriver,
IPCZ_INVALID_DRIVER_HANDLE)};
const Ref<Node> non_broker_{MakeRefCounted<Node>(Node::Type::kNormal,
kTestDriver,
IPCZ_INVALID_DRIVER_HANDLE)};
Ref<NodeLink> broker_node_link_;
Ref<NodeLink> non_broker_node_link_;
const Ref<Router> a_{MakeRefCounted<Router>()};
const Ref<Router> b_{MakeRefCounted<Router>()};
Ref<RouterLink> a_link_;
Ref<RouterLink> b_link_;
RouterLinkState* link_state_ = nullptr;
};
TEST_P(RouterLinkTest, Locking) {
EXPECT_EQ(RouterLinkState::kUnstable, link_status());
// No locking can take place until both sides are marked stable.
EXPECT_FALSE(a_link().TryLockForBypass(kTestPeer1Name));
EXPECT_FALSE(a_link().TryLockForClosure());
a_link().MarkSideStable();
b_link().MarkSideStable();
EXPECT_EQ(RouterLinkState::kStable, link_status());
// Only one side can lock for bypass, and (only) the other side can use this
// to validate the source of a future bypass request.
EXPECT_FALSE(b_link().CanNodeRequestBypass(kTestPeer1Name));
EXPECT_TRUE(a_link().TryLockForBypass(kTestPeer1Name));
EXPECT_FALSE(b_link().TryLockForBypass(kTestPeer2Name));
EXPECT_FALSE(b_link().CanNodeRequestBypass(kTestPeer2Name));
EXPECT_TRUE(b_link().CanNodeRequestBypass(kTestPeer1Name));
EXPECT_FALSE(a_link().CanNodeRequestBypass(kTestPeer1Name));
EXPECT_EQ(RouterLinkState::kStable | RouterLinkState::kLockedBySideA,
link_status());
a_link().Unlock();
EXPECT_EQ(RouterLinkState::kStable, link_status());
EXPECT_TRUE(b_link().TryLockForBypass(kTestPeer2Name));
EXPECT_FALSE(a_link().TryLockForBypass(kTestPeer1Name));
EXPECT_FALSE(a_link().CanNodeRequestBypass(kTestPeer1Name));
EXPECT_FALSE(b_link().CanNodeRequestBypass(kTestPeer2Name));
EXPECT_TRUE(a_link().CanNodeRequestBypass(kTestPeer2Name));
EXPECT_EQ(RouterLinkState::kStable | RouterLinkState::kLockedBySideB,
link_status());
b_link().Unlock();
EXPECT_EQ(RouterLinkState::kStable, link_status());
EXPECT_TRUE(a_link().TryLockForClosure());
EXPECT_FALSE(b_link().TryLockForClosure());
EXPECT_EQ(RouterLinkState::kStable | RouterLinkState::kLockedBySideA,
link_status());
a_link().Unlock();
EXPECT_EQ(RouterLinkState::kStable, link_status());
EXPECT_TRUE(b_link().TryLockForClosure());
EXPECT_FALSE(a_link().TryLockForClosure());
EXPECT_EQ(RouterLinkState::kStable | RouterLinkState::kLockedBySideB,
link_status());
b_link().Unlock();
EXPECT_EQ(RouterLinkState::kStable, link_status());
}
TEST_P(RouterLinkTest, FlushOtherSideIfWaiting) {
// FlushOtherSideIfWaiting() does nothing if the other side is not, in fact,
// waiting for something.
EXPECT_FALSE(a_link().FlushOtherSideIfWaiting());
EXPECT_FALSE(b_link().FlushOtherSideIfWaiting());
EXPECT_EQ(RouterLinkState::kUnstable, link_status());
// Mark B stable and try to lock the link. Since A is not yet stable, this
// should fail and set B's waiting bit.
b_link().MarkSideStable();
EXPECT_FALSE(b_link().TryLockForBypass(kTestPeer1Name));
EXPECT_EQ(RouterLinkState::kSideBStable | RouterLinkState::kSideBWaiting,
link_status());
// Now mark A stable. The FlushOtherSideIfWaiting() should successfully
// flush B and clear its waiting bit.
a_link().MarkSideStable();
EXPECT_EQ(RouterLinkState::kStable | RouterLinkState::kSideBWaiting,
link_status());
EXPECT_TRUE(a_link().FlushOtherSideIfWaiting());
EXPECT_EQ(RouterLinkState::kStable, link_status());
}
INSTANTIATE_TEST_SUITE_P(,
RouterLinkTest,
::testing::Values(RouterLinkTestMode::kLocal,
RouterLinkTestMode::kRemote));
} // namespace
} // namespace ipcz