blob: 445caddfbca5fad06abb35a0beb9d2521d7db1fb [file] [log] [blame]
// Copyright 2017 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 "signaling_channel.h"
#include "fake_channel_test.h"
#include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
namespace bt {
namespace l2cap {
namespace internal {
namespace {
constexpr CommandCode kUnknownCommandCode = 0x00;
constexpr CommandCode kCommandCode = 0xFF;
constexpr hci::ConnectionHandle kTestHandle = 0x0001;
constexpr uint16_t kTestMTU = 100;
class TestSignalingChannel : public SignalingChannel {
public:
explicit TestSignalingChannel(fbl::RefPtr<Channel> chan)
: SignalingChannel(std::move(chan), hci::Connection::Role::kMaster) {
set_mtu(kTestMTU);
}
~TestSignalingChannel() override = default;
using PacketCallback = fit::function<void(const SignalingPacket& packet)>;
void set_packet_callback(PacketCallback cb) { packet_cb_ = std::move(cb); }
// Expose GetNextCommandId() as public so it can be called by tests below.
using SignalingChannel::GetNextCommandId;
// Expose ResponderImpl as public so it can be directly tested (rather than
// passed to RequestDelegate).
using SignalingChannel::ResponderImpl;
private:
// SignalingChannelInterface overrides
bool SendRequest(CommandCode req_code, const ByteBuffer& payload,
ResponseHandler cb) override {
return false;
}
void ServeRequest(CommandCode req_code, RequestDelegate cb) override {}
// SignalingChannel overrides
void DecodeRxUnit(ByteBufferPtr sdu,
const SignalingPacketHandler& cb) override {
ZX_DEBUG_ASSERT(sdu);
if (sdu->size()) {
cb(SignalingPacket(sdu.get(), sdu->size() - sizeof(CommandHeader)));
} else {
// Silently drop the packet. See documentation in signaling_channel.h.
}
}
bool HandlePacket(const SignalingPacket& packet) override {
if (packet.header().code == kUnknownCommandCode)
return false;
if (packet_cb_)
packet_cb_(packet);
return true;
}
PacketCallback packet_cb_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(TestSignalingChannel);
};
class L2CAP_SignalingChannelTest : public testing::FakeChannelTest {
public:
L2CAP_SignalingChannelTest() = default;
~L2CAP_SignalingChannelTest() override = default;
protected:
void SetUp() override {
ChannelOptions options(kLESignalingChannelId);
options.conn_handle = kTestHandle;
fake_channel_inst_ = CreateFakeChannel(options);
sig_ = std::make_unique<TestSignalingChannel>(fake_channel_inst_);
}
void TearDown() override {
// Unless a test called DestroySig(), the signaling channel will outlive the
// underlying channel.
fake_channel_inst_ = nullptr;
DestroySig();
}
TestSignalingChannel* sig() const { return sig_.get(); }
void DestroySig() { sig_ = nullptr; }
private:
std::unique_ptr<TestSignalingChannel> sig_;
// Own the fake channel so that its lifetime can span beyond that of |sig_|.
fbl::RefPtr<Channel> fake_channel_inst_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(L2CAP_SignalingChannelTest);
};
TEST_F(L2CAP_SignalingChannelTest, IgnoreEmptyFrame) {
bool send_cb_called = false;
auto send_cb = [&send_cb_called](auto) { send_cb_called = true; };
fake_chan()->SetSendCallback(std::move(send_cb), dispatcher());
fake_chan()->Receive(BufferView());
RunLoopUntilIdle();
EXPECT_FALSE(send_cb_called);
}
TEST_F(L2CAP_SignalingChannelTest, Reject) {
constexpr uint8_t kTestId = 14;
// Command Reject packet.
auto expected = CreateStaticByteBuffer(
// Command header
0x01, kTestId, 0x02, 0x00,
// Reason (Command not understood)
0x00, 0x00);
// A command that TestSignalingChannel does not support.
auto cmd = CreateStaticByteBuffer(
// header
kUnknownCommandCode, kTestId, 0x04, 0x00,
// data
'L', 'O', 'L', 'Z');
EXPECT_TRUE(ReceiveAndExpect(cmd, expected));
}
TEST_F(L2CAP_SignalingChannelTest, RejectCommandCodeZero) {
constexpr uint8_t kTestId = 14;
// Command Reject packet.
auto expected = CreateStaticByteBuffer(
// Command header
0x01, kTestId, 0x02, 0x00,
// Reason (Command not understood)
0x00, 0x00);
// A command that TestSignalingChannel does not support.
auto cmd = CreateStaticByteBuffer(
// header
0x00, kTestId, 0x04, 0x00,
// data
'L', 'O', 'L', 'Z');
EXPECT_TRUE(ReceiveAndExpect(cmd, expected));
}
TEST_F(L2CAP_SignalingChannelTest, RejectNotUnderstoodWithResponder) {
constexpr uint8_t kTestId = 14;
auto expected = CreateStaticByteBuffer(
// Command header (Command Reject, ID, length)
0x01, kTestId, 0x02, 0x00,
// Reason (Command not understood)
0x00, 0x00);
bool cb_called = false;
auto send_cb = [&expected, &cb_called](auto packet) {
cb_called = true;
EXPECT_TRUE(ContainersEqual(expected, *packet));
};
fake_chan()->SetSendCallback(std::move(send_cb), dispatcher());
TestSignalingChannel::ResponderImpl responder(sig(), kCommandCode, kTestId);
responder.RejectNotUnderstood();
RunLoopUntilIdle();
EXPECT_TRUE(cb_called);
}
TEST_F(L2CAP_SignalingChannelTest, RejectInvalidCIdWithResponder) {
constexpr uint8_t kTestId = 14;
constexpr uint16_t kLocalCId = 0xf00d;
constexpr uint16_t kRemoteCId = 0xcafe;
auto expected = CreateStaticByteBuffer(
// Command header (Command Reject, ID, length)
0x01, kTestId, 0x06, 0x00,
// Reason (Invalid channel ID)
0x02, 0x00,
// Data (Channel IDs),
LowerBits(kLocalCId), UpperBits(kLocalCId), LowerBits(kRemoteCId),
UpperBits(kRemoteCId));
bool cb_called = false;
auto send_cb = [&expected, &cb_called](auto packet) {
cb_called = true;
EXPECT_TRUE(ContainersEqual(expected, *packet));
};
fake_chan()->SetSendCallback(std::move(send_cb), dispatcher());
TestSignalingChannel::ResponderImpl responder(sig(), kCommandCode, kTestId);
responder.RejectInvalidChannelId(kLocalCId, kRemoteCId);
RunLoopUntilIdle();
EXPECT_TRUE(cb_called);
}
TEST_F(L2CAP_SignalingChannelTest, InvalidMTU) {
constexpr uint8_t kTestId = 14;
constexpr uint16_t kTooSmallMTU = 7;
// Command Reject packet.
auto expected = CreateStaticByteBuffer(
// Command header
0x01, kTestId, 0x04, 0x00,
// Reason (Signaling MTU exceeded)
0x01, 0x00,
// The supported MTU
static_cast<uint8_t>(kTooSmallMTU), 0x00);
// A command that is one octet larger than the MTU.
auto cmd = CreateStaticByteBuffer(
// header
kCommandCode, kTestId, 0x04, 0x00,
// data
'L', 'O', 'L', 'Z');
sig()->set_mtu(kTooSmallMTU);
EXPECT_TRUE(ReceiveAndExpect(cmd, expected));
}
TEST_F(L2CAP_SignalingChannelTest, HandlePacket) {
constexpr uint8_t kTestId = 14;
// A command that TestSignalingChannel supports.
auto cmd = CreateStaticByteBuffer(
// header
kCommandCode, kTestId, 0x04, 0x00,
// data
'L', 'O', 'L', 'Z');
bool called = false;
sig()->set_packet_callback([&cmd, &called](auto packet) {
EXPECT_TRUE(ContainersEqual(cmd, packet.data()));
called = true;
});
fake_chan()->Receive(cmd);
RunLoopUntilIdle();
EXPECT_TRUE(called);
}
TEST_F(L2CAP_SignalingChannelTest, UseChannelAfterSignalFree) {
// Destroy the underlying channel's user (SignalingChannel).
DestroySig();
// Ensure that the underlying channel is still alive.
ASSERT_TRUE(static_cast<bool>(fake_chan()));
// SignalingChannel is expected to deactivate the channel if it doesn't own
// it. Either way, the channel isn't in a state that can receive test data.
EXPECT_FALSE(fake_chan()->activated());
// Ensure that closing the channel (possibly firing callback) is OK.
fake_chan()->Close();
RunLoopUntilIdle();
}
TEST_F(L2CAP_SignalingChannelTest, ValidRequestCommandIds) {
EXPECT_EQ(0x01, sig()->GetNextCommandId());
for (int i = 0; i < 256; i++) {
EXPECT_NE(0x00, sig()->GetNextCommandId());
}
}
} // namespace
} // namespace internal
} // namespace l2cap
} // namespace bt