blob: 20c718675131ce18494f8982485834da2fa59463 [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/sm/pairing_phase.h"
#include <memory>
#include <gtest/gtest.h>
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/macros.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/fake_channel_test.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/sm/fake_phase_listener.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/sm/packet.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/sm/smp.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/sm/types.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
namespace bt::sm {
namespace {
using Listener = PairingPhase::Listener;
using PairingChannelHandler = PairingChannel::Handler;
class ConcretePairingPhase : public PairingPhase, public PairingChannelHandler {
public:
ConcretePairingPhase(PairingChannel::WeakPtr chan,
Listener::WeakPtr listener,
Role role,
size_t max_packet_size = sizeof(PairingPublicKeyParams))
: PairingPhase(std::move(chan), std::move(listener), role),
weak_handler_(this) {
// All concrete pairing phases should set themselves as the pairing channel
// handler.
SetPairingChannelHandler(*this);
last_rx_packet_ = DynamicByteBuffer(max_packet_size);
}
// All concrete pairing phases should invalidate the channel handler in their
// destructor.
~ConcretePairingPhase() override { InvalidatePairingChannelHandler(); }
// PairingPhase overrides.
std::string ToStringInternal() override { return ""; }
// PairingPhase override, not tested as PairingPhase does not implement this
// pure virtual function.
void Start() override {}
// PairingChannelHandler override
void OnChannelClosed() override { PairingPhase::HandleChannelClosed(); }
void OnRxBFrame(ByteBufferPtr sdu) override { sdu->Copy(&last_rx_packet_); }
const ByteBuffer& last_rx_packet() { return last_rx_packet_; }
private:
WeakSelf<PairingChannelHandler> weak_handler_;
DynamicByteBuffer last_rx_packet_;
};
class PairingPhaseTest : public l2cap::testing::FakeChannelTest {
public:
PairingPhaseTest() = default;
~PairingPhaseTest() override = default;
protected:
void SetUp() override { NewPairingPhase(); }
void TearDown() override { pairing_phase_ = nullptr; }
void NewPairingPhase(Role role = Role::kInitiator,
bt::LinkType ll_type = bt::LinkType::kLE) {
l2cap::ChannelId cid = ll_type == bt::LinkType::kLE ? l2cap::kLESMPChannelId
: l2cap::kSMPChannelId;
ChannelOptions options(cid);
options.link_type = ll_type;
listener_ = std::make_unique<FakeListener>();
fake_chan_ = CreateFakeChannel(options);
sm_chan_ = std::make_unique<PairingChannel>(fake_chan_->GetWeakPtr());
pairing_phase_ = std::make_unique<ConcretePairingPhase>(
sm_chan_->GetWeakPtr(), listener_->as_weak_ptr(), role);
}
l2cap::testing::FakeChannel* fake_chan() const { return fake_chan_.get(); }
FakeListener* listener() { return listener_.get(); }
ConcretePairingPhase* pairing_phase() { return pairing_phase_.get(); }
private:
std::unique_ptr<FakeListener> listener_;
std::unique_ptr<l2cap::testing::FakeChannel> fake_chan_;
std::unique_ptr<PairingChannel> sm_chan_;
std::unique_ptr<ConcretePairingPhase> pairing_phase_;
BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(PairingPhaseTest);
};
using PairingPhaseDeathTest = PairingPhaseTest;
TEST_F(PairingPhaseDeathTest, CallMethodOnFailedPhaseDies) {
pairing_phase()->Abort(ErrorCode::kUnspecifiedReason);
ASSERT_DEATH_IF_SUPPORTED(
pairing_phase()->OnFailure(Error(HostError::kFailed)), ".*failed.*");
}
TEST_F(PairingPhaseTest, ChannelClosedNotifiesListener) {
ASSERT_EQ(listener()->pairing_error_count(), 0);
fake_chan()->Close();
RunUntilIdle();
ASSERT_EQ(listener()->pairing_error_count(), 1);
EXPECT_EQ(Error(HostError::kLinkDisconnected), listener()->last_error());
}
TEST_F(PairingPhaseTest, OnFailureNotifiesListener) {
auto ecode = ErrorCode::kDHKeyCheckFailed;
ASSERT_EQ(listener()->pairing_error_count(), 0);
pairing_phase()->OnFailure(Error(ecode));
RunUntilIdle();
ASSERT_EQ(listener()->pairing_error_count(), 1);
EXPECT_EQ(Error(ecode), listener()->last_error());
}
TEST_F(PairingPhaseTest, AbortSendsFailureMessageAndNotifiesListener) {
ByteBufferPtr msg_sent = nullptr;
fake_chan()->SetSendCallback(
[&msg_sent](ByteBufferPtr sdu) { msg_sent = std::move(sdu); },
dispatcher());
ASSERT_EQ(0, listener()->pairing_error_count());
pairing_phase()->Abort(ErrorCode::kDHKeyCheckFailed);
RunUntilIdle();
// Check the PairingFailed message was sent to the channel
ASSERT_TRUE(msg_sent);
auto reader = PacketReader(msg_sent.get());
ASSERT_EQ(ErrorCode::kDHKeyCheckFailed, reader.payload<ErrorCode>());
// Check the listener PairingFailed callback was made.
ASSERT_EQ(1, listener()->pairing_error_count());
EXPECT_EQ(Error(ErrorCode::kDHKeyCheckFailed), listener()->last_error());
}
} // namespace
} // namespace bt::sm