blob: 147e36d4cae2978446174bc0d6d5225ed152dd69 [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 "le_signaling_channel.h"
#include "garnet/drivers/bluetooth/lib/common/test_helpers.h"
#include "lib/fxl/arraysize.h"
#include "fake_channel_test.h"
namespace btlib {
namespace l2cap {
namespace internal {
namespace {
constexpr hci::ConnectionHandle kTestHandle = 0x0001;
constexpr uint8_t kTestCmdId = 1;
template <hci::Connection::Role Role = hci::Connection::Role::kMaster>
class LESignalingChannelTest : public testing::FakeChannelTest {
public:
LESignalingChannelTest() = default;
~LESignalingChannelTest() override = default;
protected:
void SetUp() override {
ChannelOptions options(kLESignalingChannelId);
options.conn_handle = kTestHandle;
auto fake_chan = CreateFakeChannel(options);
sig_ = std::make_unique<LESignalingChannel>(std::move(fake_chan), Role);
}
void TearDown() override { sig_ = nullptr; }
LESignalingChannel* sig() const { return sig_.get(); }
private:
std::unique_ptr<LESignalingChannel> sig_;
FXL_DISALLOW_COPY_AND_ASSIGN(LESignalingChannelTest);
};
using L2CAP_LESignalingChannelTest = LESignalingChannelTest<>;
using L2CAP_LESignalingChannelSlaveTest =
LESignalingChannelTest<hci::Connection::Role::kSlave>;
TEST_F(L2CAP_LESignalingChannelTest, 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(common::BufferView());
RunLoopUntilIdle();
EXPECT_FALSE(send_cb_called);
}
TEST_F(L2CAP_LESignalingChannelTest, RejectMalformedTooLarge) {
// Command Reject packet.
// clang-format off
auto expected = common::CreateStaticByteBuffer(
// Command header
0x01, kTestCmdId, 0x02, 0x00,
// Reason (Command not understood)
0x00, 0x00);
// Header-encoded length is less than the otherwise-valid Connection Parameter
// Update packet's payload size.
auto cmd_with_oversize_payload = common::CreateStaticByteBuffer(
0x12, kTestCmdId, 0x07, 0x00,
// Valid connection parameters
0x06, 0x00,
0x80, 0x0C,
0xF3, 0x01,
0x80, 0x0C);
// clang-format on
EXPECT_TRUE(ReceiveAndExpect(cmd_with_oversize_payload, expected));
}
TEST_F(L2CAP_LESignalingChannelTest, RejectMalformedTooSmall) {
// Command Reject packet.
// clang-format off
auto expected = common::CreateStaticByteBuffer(
// Command header
0x01, kTestCmdId, 0x02, 0x00,
// Reason (Command not understood)
0x00, 0x00);
// Header-encoded length is more than the otherwise-valid Connection Parameter
// Update packet's payload size.
auto cmd_with_undersize_payload = common::CreateStaticByteBuffer(
0x12, kTestCmdId, 0x09, 0x00,
// Valid connection parameters
0x06, 0x00,
0x80, 0x0C,
0xF3, 0x01,
0x80, 0x0C);
// clang-format on
EXPECT_TRUE(ReceiveAndExpect(cmd_with_undersize_payload, expected));
}
TEST_F(L2CAP_LESignalingChannelTest, DefaultMTU) {
constexpr size_t kCommandSize = kMinLEMTU + 1;
// The channel should start out with the minimum MTU as the default (23
// octets).
common::StaticByteBuffer<kCommandSize> cmd;
// Make sure that the packet is well formed (the command code does not
// matter).
MutableSignalingPacket packet(&cmd, kCommandSize - sizeof(CommandHeader));
packet.mutable_header()->id = kTestCmdId;
packet.mutable_header()->length = htole16(packet.payload_size());
// Command Reject packet.
// clang-format off
auto expected = common::CreateStaticByteBuffer(
// Command header
0x01, kTestCmdId, 0x04, 0x00,
// Reason (Signaling MTU exceeded)
0x01, 0x00,
// The supported MTU (23)
0x17, 0x00);
// clang-format on
EXPECT_TRUE(ReceiveAndExpect(cmd, expected));
}
TEST_F(L2CAP_LESignalingChannelTest, UnknownCommand) {
// All unsupported commands must be rejected.
const std::set<CommandCode> supported{kConnectionParameterUpdateRequest};
for (CommandCode code = 0; code < std::numeric_limits<CommandCode>::max();
code++) {
// Skip supported commands as they are tested separately.
if (supported.count(code))
continue;
// Use command code as ID.
auto cmd = common::CreateStaticByteBuffer(code, code, 0x00, 0x00);
// Expected
// clang-format off
auto expected = common::CreateStaticByteBuffer(
// Command header
0x01, code, 0x02, 0x00,
// Reason (Command not understood)
0x00, 0x00);
// clang-format on
ASSERT_TRUE(ReceiveAndExpect(cmd, expected));
}
}
TEST_F(L2CAP_LESignalingChannelTest, ConnParamUpdateMalformedPayloadTooLarge) {
// Packet size larger than conn. param. update payload.
// clang-format off
auto cmd = common::CreateStaticByteBuffer(
0x12, kTestCmdId, 0x09, 0x00,
// Valid conn. param. values:
0x06, 0x00,
0x80, 0x0C,
0xF3, 0x01,
0x0A, 0x00,
// Extra byte
0x00);
auto expected = common::CreateStaticByteBuffer(
// Command header
0x01, kTestCmdId, 0x02, 0x00,
// Reason (Command not understood)
0x00, 0x00);
// clang-format on
EXPECT_TRUE(ReceiveAndExpect(cmd, expected));
}
TEST_F(L2CAP_LESignalingChannelTest, ConnParamUpdateMalformedPayloadTooSmall) {
// Packet size larger than conn. param. update payload.
// clang-format off
auto cmd = common::CreateStaticByteBuffer(
0x12, kTestCmdId, 0x07, 0x00,
// Valid conn. param. values:
0x06, 0x00,
0x80, 0x0C,
0xF3, 0x01,
0x0A/*0x00 // Missing a byte */);
auto expected = common::CreateStaticByteBuffer(
// Command header
0x01, kTestCmdId, 0x02, 0x00,
// Reason (Command not understood)
0x00, 0x00);
// clang-format on
EXPECT_TRUE(ReceiveAndExpect(cmd, expected));
}
TEST_F(L2CAP_LESignalingChannelTest, ConnParamUpdateReject) {
// clang-format off
common::StaticByteBuffer<12> commands[] = {
common::CreateStaticByteBuffer(
0x12, kTestCmdId, 0x08, 0x00,
0x07, 0x00, // interval min larger than max (both within range)
0x06, 0x00,
0xF3, 0x01,
0x0A, 0x00),
common::CreateStaticByteBuffer(
0x12, kTestCmdId, 0x08, 0x00,
0x05, 0x00, // interval min too small
0x80, 0x0C,
0xF3, 0x01,
0x0A, 0x00),
common::CreateStaticByteBuffer(
0x12, kTestCmdId, 0x08, 0x00,
0x06, 0x00,
0x81, 0x0C, // interval max too large
0xF3, 0x01,
0x0A, 0x00),
common::CreateStaticByteBuffer(
0x12, kTestCmdId, 0x08, 0x00,
0x06, 0x00,
0x80, 0x0C,
0xF4, 0x01, // Latency too large
0x0A, 0x00),
common::CreateStaticByteBuffer(
0x12, kTestCmdId, 0x08, 0x00,
0x06, 0x00,
0x80, 0x0C,
0xF3, 0x01,
0x09, 0x00), // Supv. timeout too small
common::CreateStaticByteBuffer(
0x12, kTestCmdId, 0x08, 0x00,
0x06, 0x00,
0x80, 0x0C,
0xF3, 0x01,
0x81, 0x0C) // Supv. timeout too large
};
// clang-format on
for (size_t i = 0; i < arraysize(commands); ++i) {
// Conn. param. update response
// clang-format off
auto expected = common::CreateStaticByteBuffer(
// Command header
0x13, kTestCmdId, 0x02, 0x00,
// Rejected
0x01, 0x00);
// clang-format on
ASSERT_TRUE(ReceiveAndExpect(commands[i], expected));
}
}
TEST_F(L2CAP_LESignalingChannelTest, ConnParamUpdateAccept) {
// clang-format off
auto cmd = common::CreateStaticByteBuffer(
0x12, kTestCmdId, 0x08, 0x00,
// Valid connection parameters
0x06, 0x00,
0x80, 0x0C,
0xF3, 0x01,
0x80, 0x0C);
// Conn. param. update response
auto expected = common::CreateStaticByteBuffer(
// Command header
0x13, kTestCmdId, 0x02, 0x00,
// Accepted
0x00, 0x00);
// clang-format on
bool fake_chan_cb_called = false;
auto fake_chan_cb = [&expected, &fake_chan_cb_called, this](auto packet) {
EXPECT_TRUE(common::ContainersEqual(expected, *packet));
fake_chan_cb_called = true;
};
bool conn_param_cb_called = false;
auto conn_param_cb = [&conn_param_cb_called, this](const auto& params) {
EXPECT_EQ(0x0006, params.min_interval());
EXPECT_EQ(0x0C80, params.max_interval());
EXPECT_EQ(0x01F3, params.max_latency());
EXPECT_EQ(0x0C80, params.supervision_timeout());
conn_param_cb_called = true;
};
fake_chan()->SetSendCallback(fake_chan_cb, dispatcher());
sig()->set_conn_param_update_callback(conn_param_cb, dispatcher());
fake_chan()->Receive(cmd);
RunLoopUntilIdle();
EXPECT_TRUE(fake_chan_cb_called);
EXPECT_TRUE(conn_param_cb_called);
}
TEST_F(L2CAP_LESignalingChannelSlaveTest, ConnParamUpdateReject) {
// clang-format off
auto cmd = common::CreateStaticByteBuffer(
0x12, kTestCmdId, 0x08, 0x00,
// Valid connection parameters
0x06, 0x00,
0x80, 0x0C,
0xF3, 0x01,
0x80, 0x0C);
// Command rejected
auto expected = common::CreateStaticByteBuffer(
// Command header
0x01, kTestCmdId, 0x02, 0x00,
// Reason (Command not understood)
0x00, 0x00);
// clang-format on
bool cb_called = false;
auto cb = [&expected, &cb_called, this](auto packet) {
EXPECT_TRUE(common::ContainersEqual(expected, *packet));
cb_called = true;
};
fake_chan()->SetSendCallback(cb, dispatcher());
fake_chan()->Receive(cmd);
RunLoopUntilIdle();
EXPECT_TRUE(cb_called);
}
} // namespace
} // namespace internal
} // namespace l2cap
} // namespace btlib