blob: 094e03350ae6c6aea2516974af5f8bcf8b9fe4c1 [file] [log] [blame]
// Copyright 2018 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/sdp/data_element.h"
#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/sdp/sdp.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
namespace bt::sdp {
namespace {
template <std::size_t N>
DynamicByteBuffer make_dynamic_byte_buffer(std::array<uint8_t, N> bytes) {
DynamicByteBuffer ret(N);
ret.Write(bytes.data(), N);
return ret;
}
TEST(DataElementTest, CreateIsNull) {
DataElement elem;
EXPECT_EQ(DataElement::Type::kNull, elem.type());
EXPECT_TRUE(elem.Get<std::nullptr_t>());
EXPECT_EQ(nullptr, *elem.Get<std::nullptr_t>());
StaticByteBuffer expected(0x00);
DynamicByteBuffer buf(1);
EXPECT_EQ(1u, elem.Write(&buf));
EXPECT_TRUE(ContainersEqual(expected, buf));
}
TEST(DataElementTest, SetAndGet) {
DataElement elem;
elem.Set(uint8_t{5});
EXPECT_TRUE(elem.Get<uint8_t>());
EXPECT_EQ(5u, *elem.Get<uint8_t>());
EXPECT_FALSE(elem.Get<int16_t>());
elem.Set(std::string("Fuchsiađź’–"));
EXPECT_FALSE(elem.Get<uint8_t>());
EXPECT_TRUE(elem.Get<std::string>());
EXPECT_EQ(std::string("Fuchsiađź’–"), *elem.Get<std::string>());
}
TEST(DataElementTest, Read) {
StaticByteBuffer buf(
0x25, // Type (4: String) & Size (5: in an additional byte) = 0b00100 101
0x0B, // Bytes
'F',
'u',
'c',
'h',
's',
'i',
'a',
0xF0,
0x9F,
0x92,
0x96, // String
0xDE,
0xAD,
0xBE,
0xEF // Extra data (shouldn't be parsed)
);
DataElement elem;
EXPECT_EQ(13u, DataElement::Read(&elem, buf));
EXPECT_EQ(DataElement::Type::kString, elem.type());
EXPECT_EQ(std::string("Fuchsiađź’–"), *elem.Get<std::string>());
// Invalid - 0xDE: 0x11011 110 = 37 (invalid) + 6 (2 following byte size)
EXPECT_EQ(0u, DataElement::Read(&elem, buf.view(13)));
// elem shouldn't have been touched
EXPECT_EQ(DataElement::Type::kString, elem.type());
EXPECT_EQ(std::string("Fuchsiađź’–"), *elem.Get<std::string>());
}
TEST(DataElementTest, ReadInvalidType) {
StaticByteBuffer buf(
0xFD, // Type (Invalid) & Size (5: in an additional byte) = 0b11111 101
0x0B, // Bytes
'F',
'u',
'c',
'h',
's',
'i',
'a',
0xF0,
0x9F,
0x92,
0x96 // String
);
DataElement elem;
EXPECT_EQ(0u, DataElement::Read(&elem, buf));
}
TEST(DataElementTest, ReadUUID) {
StaticByteBuffer buf(
0x19, // Type (3: UUID) & Size (1: two bytes) = 0b00011 001
0x01,
0x00 // L2CAP
);
DataElement elem;
EXPECT_EQ(3u, DataElement::Read(&elem, buf));
EXPECT_EQ(DataElement::Type::kUuid, elem.type());
EXPECT_EQ(UUID(uint16_t{0x0100}), *elem.Get<UUID>());
StaticByteBuffer buf2(
0x1A, // Type (3: UUID) & Size (2: four bytes) = 0b00011 010
0x01,
0x02,
0x03,
0x04);
EXPECT_EQ(5u, DataElement::Read(&elem, buf2));
EXPECT_EQ(DataElement::Type::kUuid, elem.type());
EXPECT_EQ(UUID(uint32_t{0x01020304}), *elem.Get<UUID>());
StaticByteBuffer buf3(
0x1B, // Type (3: UUID) & Size (3: eight bytes) = 0b00011 011
0x01,
0x02,
0x03,
0x04,
0x01,
0x02,
0x03,
0x04,
0x01,
0x02,
0x03,
0x04,
0x01,
0x02,
0x03,
0x04);
EXPECT_EQ(0u, DataElement::Read(&elem, buf3));
StaticByteBuffer buf4(
0x1C, // Type (3: UUID) & Size (3: eight bytes) = 0b00011 100
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0A,
0x0B,
0x0C,
0x0D,
0x0E,
0x0F,
0x10);
EXPECT_EQ(17u, DataElement::Read(&elem, buf4));
EXPECT_EQ(DataElement::Type::kUuid, elem.type());
// UInt128 in UUID is little-endian
EXPECT_EQ(UUID({0x10,
0x0F,
0x0E,
0x0D,
0x0C,
0x0B,
0x0A,
0x09,
0x08,
0x07,
0x06,
0x05,
0x04,
0x03,
0x02,
0x01}),
*elem.Get<UUID>());
}
TEST(DataElementTest, Write) {
// This represents a plausible attribute_lists parameter of a
// SDP_ServiceSearchAttributeResponse PDU for an SPP service.
std::vector<DataElement> attribute_list;
// SerialPort from Assigned Numbers
std::vector<DataElement> service_class_list;
service_class_list.emplace_back(DataElement(UUID(uint16_t{0x1101})));
DataElement service_class_value(std::move(service_class_list));
attribute_list.emplace_back(DataElement(kServiceClassIdList));
attribute_list.emplace_back(std::move(service_class_value));
// Protocol Descriptor List
std::vector<DataElement> protocol_list_value;
// ( L2CAP, PSM=RFCOMM )
std::vector<DataElement> protocol_l2cap;
protocol_l2cap.emplace_back(DataElement(protocol::kL2CAP));
protocol_l2cap.emplace_back(DataElement(uint16_t{0x0003})); // RFCOMM
protocol_list_value.emplace_back(DataElement(std::move(protocol_l2cap)));
// ( RFCOMM, CHANNEL=1 )
std::vector<DataElement> protocol_rfcomm;
protocol_rfcomm.push_back(DataElement(protocol::kRFCOMM));
protocol_rfcomm.push_back(DataElement(uint8_t{1})); // Server Channel = 1
protocol_list_value.emplace_back(DataElement(std::move(protocol_rfcomm)));
attribute_list.emplace_back(DataElement(kProtocolDescriptorList));
attribute_list.emplace_back(DataElement(std::move(protocol_list_value)));
// Bluetooth Profile Descriptor List
std::vector<DataElement> profile_sequence_list;
std::vector<DataElement> spp_sequence;
spp_sequence.push_back(DataElement(UUID(uint16_t{0x1101})));
spp_sequence.push_back(DataElement(uint16_t{0x0102}));
profile_sequence_list.emplace_back(std::move(spp_sequence));
attribute_list.push_back(DataElement(kBluetoothProfileDescriptorList));
attribute_list.push_back((DataElement(std::move(profile_sequence_list))));
// A custom attribute that has a uint64_t in it.
attribute_list.emplace_back(DataElement(UUID(uint16_t(0xF00D))));
attribute_list.emplace_back(DataElement(uint64_t(0xB0BAC0DECAFEFACE)));
DataElement attribute_lists_elem(std::move(attribute_list));
// clang-format off
StaticByteBuffer expected(
0x35, 0x35, // Sequence uint8 41 bytes
0x09, // uint16_t type
UpperBits(kServiceClassIdList), LowerBits(kServiceClassIdList),
0x35, 0x03, // Sequence uint8 3 bytes
0x19, // UUID (16 bits)
0x11, 0x01, // Serial Port from assigned numbers
0x09, // uint16_t type
UpperBits(kProtocolDescriptorList), LowerBits(kProtocolDescriptorList),
0x35, 0x0F, // Sequence uint8 15 bytes
0x35, 0x06, // Sequence uint8 6 bytes
0x19, // Type: UUID (16 bits)
0x01, 0x00, // L2CAP UUID
0x09, // Type: uint16_t
0x00, 0x03, // RFCOMM PSM
0x35, 0x05, // Sequence uint8 5 bytes
0x19, // Type: UUID (16 bits)
0x00, 0x03, // RFCOMM UUID
0x08, // Type: uint8_t
0x01, // RFCOMM Channel 1
0x09, // uint16_t type
UpperBits(kBluetoothProfileDescriptorList),
LowerBits(kBluetoothProfileDescriptorList),
0x35, 0x08, // Sequence uint8 8 bytes
0x35, 0x06, // Sequence uint8 6 bytes
0x19, // Type: UUID (16 bits)
0x11, 0x01, // 0x1101 (SPP)
0x09, // Type: uint16_t
0x01, 0x02, // v1.2
0x19, // Type: UUID (16 bits)
0xF0, 0x0D, // Custom attribute ID,
0x0B, // uint64_t type
0xB0, 0xBA, 0xC0, 0xDE, 0xCA, 0xFE, 0xFA, 0xCE // Data for uint64_t
);
// clang-format on
DynamicByteBuffer block(55);
size_t written = attribute_lists_elem.Write(&block);
EXPECT_EQ(expected.size(), written);
EXPECT_EQ(written, attribute_lists_elem.WriteSize());
EXPECT_TRUE(ContainersEqual(expected, block));
}
TEST(DataElementTest, ReadSequence) {
// clang-format off
StaticByteBuffer buf(
0x35, 0x08, // Sequence with 1 byte length (8)
0x09, 0x00, 0x01, // uint16_t: 1
0x0A, 0x00, 0x00, 0x00, 0x02 // uint32_t: 2
);
// clang-format on
DataElement elem;
EXPECT_EQ(buf.size(), DataElement::Read(&elem, buf));
EXPECT_EQ(DataElement::Type::kSequence, elem.type());
auto* it = elem.At(0);
EXPECT_EQ(DataElement::Type::kUnsignedInt, it->type());
EXPECT_EQ(1u, *it->Get<uint16_t>());
it = elem.At(1);
EXPECT_EQ(DataElement::Type::kUnsignedInt, it->type());
EXPECT_EQ(2u, *it->Get<uint32_t>());
}
TEST(DataElementTest, ReadNestedSequence) {
StaticByteBuffer buf(0x35,
0x1C, // Sequence uint8 28 bytes
// Sequence 0
0x35,
0x08, // Sequence uint8 8 bytes
0x09,
0x00,
0x00, // Element: uint16_t (0)
0x0A,
0xFE,
0xED,
0xBE,
0xEF, // Element: uint32_t (0xFEEDBEEF)
// Sequence 1
0x35,
0x10, // Sequence uint8 16 bytes
0x09,
0x00,
0x00, // Element: uint16_t (0)
0x0A,
0xFE,
0xDB,
0xAC,
0x01, // Element: uint32_t (0xFEDBAC01)
0x09,
0x00,
0x01, // Handle: uint16_t (1 = kServiceClassIdList)
0x35,
0x03,
0x19,
0x11,
0x01 // Element: Sequence (3) { UUID(0x1101) }
);
DataElement elem;
EXPECT_EQ(buf.size(), DataElement::Read(&elem, buf));
EXPECT_EQ(DataElement::Type::kSequence, elem.type());
auto* outer_it = elem.At(0);
EXPECT_EQ(DataElement::Type::kSequence, outer_it->type());
auto* it = outer_it->At(0);
EXPECT_EQ(0u, *it->Get<uint16_t>());
it = outer_it->At(1);
EXPECT_EQ(0xfeedbeef, *it->Get<uint32_t>());
outer_it = elem.At(1);
EXPECT_EQ(DataElement::Type::kSequence, outer_it->type());
it = outer_it->At(0);
EXPECT_EQ(DataElement::Type::kUnsignedInt, it->type());
EXPECT_EQ(0u, *it->Get<uint16_t>());
it = outer_it->At(1);
EXPECT_EQ(DataElement::Type::kUnsignedInt, it->type());
EXPECT_EQ(0xfedbac01, *it->Get<uint32_t>());
it = outer_it->At(2);
EXPECT_EQ(DataElement::Type::kUnsignedInt, it->type());
EXPECT_EQ(1u, *it->Get<uint16_t>());
it = outer_it->At(3);
EXPECT_EQ(DataElement::Type::kSequence, it->type());
auto inner_it = it->At(0);
EXPECT_EQ(DataElement::Type::kUuid, inner_it->type());
}
TEST(DataElementTest, ToString) {
EXPECT_EQ("Null", DataElement().ToString());
EXPECT_EQ("Boolean(true)", DataElement(true).ToString());
EXPECT_EQ("UnsignedInt:1(27)", DataElement(uint8_t{27}).ToString());
EXPECT_EQ("SignedInt:4(-54321)", DataElement(int32_t{-54321}).ToString());
EXPECT_EQ("UUID(00000100-0000-1000-8000-00805f9b34fb)",
DataElement(protocol::kL2CAP).ToString());
EXPECT_EQ("String(fuchsiađź’–)",
DataElement(std::string("fuchsiađź’–")).ToString());
// This test and the following one print invalid unicode strings by replacing
// nonASCII characters
// with '.'. Somewhat confusingly, individual bytes of invalid unicode
// sequences can be valid
// ASCII bytes. In particular, the '\x28' in the invalid unicode sequences
// below is a perfectly valid '(' in ASCII, so it prints as that.
EXPECT_EQ(
"String(ABC.(XYZ)",
DataElement(std::string("ABC\xc3\x28XYZ")).ToString()); // Invalid UTF8.
EXPECT_EQ("String(ABC.(XYZ....)",
DataElement(std::string("ABC\xc3\x28XYZđź’–"))
.ToString()); // Invalid UTF8 means the whole
// string must be treated as ASCII.
DataElement elem;
elem.SetUrl(std::string("http://example.com"));
EXPECT_EQ("Url(http://example.com)", elem.ToString());
std::vector<DataElement> strings;
strings.emplace_back(std::string("hello"));
strings.emplace_back(std::string("sapphiređź”·"));
EXPECT_EQ("Sequence { String(hello) String(sapphiređź”·) }",
DataElement(std::move(strings)).ToString());
DataElement alts;
strings.clear();
strings.emplace_back(std::string("hello"));
strings.emplace_back(std::string("sapphiređź”·"));
alts.SetAlternative(std::move(strings));
EXPECT_EQ("Alternatives { String(hello) String(sapphiređź”·) }",
alts.ToString());
}
TEST(DataElementTest, SetAndGetUrl) {
DataElement elem;
elem.SetUrl(std::string("https://foobar.dev"));
EXPECT_FALSE(elem.Get<std::string>());
EXPECT_EQ(DataElement::Type::kUrl, elem.type());
EXPECT_EQ(std::string("https://foobar.dev"), *elem.GetUrl());
}
TEST(DataElementTest, SetInvalidUrlStringIsNoOp) {
DataElement elem;
EXPECT_EQ(DataElement::Type::kNull, elem.type());
elem.SetUrl(std::string("https://foobarđź”·.dev"));
EXPECT_FALSE(elem.GetUrl());
EXPECT_EQ(DataElement::Type::kNull, elem.type());
}
TEST(DataElementTest, ReadUrlFromBuffer) {
StaticByteBuffer buf(
0x45, // Type (8: URL) & Size (5: in an additional byte) = 0b01000 101
0x0B, // 11 Bytes
'F',
'u',
'c',
'h',
's',
'i',
'a',
'.',
'd',
'e',
'v', // URL String
0xDE,
0xAD,
0xBE,
0xEF // Extra data (shouldn't be parsed)
);
DataElement read_elem;
EXPECT_EQ(13u, DataElement::Read(&read_elem, buf));
EXPECT_EQ(DataElement::Type::kUrl, read_elem.type());
EXPECT_EQ(std::string("Fuchsia.dev"), *read_elem.GetUrl());
}
TEST(DataElementTest, WriteUrlToBuffer) {
DataElement url_elem;
url_elem.SetUrl(std::string("Test.com"));
auto expected = StaticByteBuffer(
0x45, // Type (8: URL) & Size (5: in an additional byte) = 0b01000 101
0x08, // 8 Bytes
'T',
'e',
's',
't',
'.',
'c',
'o',
'm' // URL String
);
DynamicByteBuffer write_buf(10);
size_t written = url_elem.Write(&write_buf);
EXPECT_EQ(expected.size(), written);
EXPECT_EQ(written, url_elem.WriteSize());
EXPECT_TRUE(ContainersEqual(expected, write_buf));
}
TEST(DataElementTest, SetAndGetStrings) {
auto buffer_set_string = make_dynamic_byte_buffer<10>(
{'s', 'e', 't', ' ', 's', 't', 'r', 'i', 'n', 'g'});
std::string string_set_string("set string");
auto buffer_set_buffer = make_dynamic_byte_buffer<10>(
{'s', 'e', 't', ' ', 'b', 'u', 'f', 'f', 'e', 'r'});
std::string string_set_buffer("set buffer");
DataElement elem_set_string;
elem_set_string.Set(string_set_string);
EXPECT_EQ(elem_set_string.Get<std::string>(), string_set_string);
EXPECT_EQ(elem_set_string.Get<DynamicByteBuffer>(), buffer_set_string);
DataElement elem_set_buffer;
elem_set_buffer.Set(string_set_buffer);
EXPECT_EQ(elem_set_buffer.Get<DynamicByteBuffer>(), buffer_set_buffer);
EXPECT_EQ(elem_set_buffer.Get<std::string>(), string_set_buffer);
}
} // namespace
} // namespace bt::sdp