/* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "common/data_element_reader.h" namespace bluetooth { namespace sdp { using namespace testing; using DataElement = DataElementReader::DataElement; // A helper class to help work with the Data Element classes. class ReaderPacket : public ::bluetooth::Packet { public: using Packet::Packet; static std::shared_ptr Make(std::vector payload) { auto pkt = std::shared_ptr(new ReaderPacket()); pkt->packet_start_index_ = 0; pkt->packet_end_index_ = payload.size(); pkt->data_ = std::make_shared>(std::move(payload)); return pkt; } std::string ToString() const override { return ""; } bool IsValid() const override { return true; } std::pair GetPayloadIndecies() const override { return std::pair(packet_start_index_, packet_end_index_); } }; bool operator!=(DataElementReader a, DataElementReader b); // A helper function to help compare DataElementReader objects. bool operator==(DataElementReader a, DataElementReader b) { while (true) { DataElement a_elem = a.ReadNext(); DataElement b_elem = b.ReadNext(); if (a_elem != b_elem) return false; // If we get here that means both a and b have reached the end. if (a_elem == DataElement(std::monostate())) break; } return true; } bool operator!=(DataElementReader a, DataElementReader b) { return !(a == b); } // A helper function to convert a type and a size to a descriptor byte. constexpr uint8_t Desc(DataElementType t, DataElementSize s) { return static_cast(t) << 3 | static_cast(s); } // Helper that can create a Data Element reader from a vector. DataElementReader CreateReader(std::vector payload) { auto packet = ReaderPacket::Make(std::move(payload)); return DataElementReader(packet->begin(), packet->end()); } // Test all the valid cases of reading the next Data Element. using ValidTestParam = std::tuple, DataElement>; class ValidReadTest : public TestWithParam {}; std::vector valid_values = { // Boolean Tests ValidTestParam{ {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01}, true, }, ValidTestParam{ {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00}, false, }, // Signed Integer Tests ValidTestParam{ {Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE1), 0xFF}, static_cast(-1)}, ValidTestParam{ {Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE2), 0xFF, 0xFF}, static_cast(-1)}, ValidTestParam{{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE4), 0xFF, 0xFF, 0xFF, 0xFF}, static_cast(-1)}, ValidTestParam{{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE8), 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, static_cast(-1)}, ValidTestParam{{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE16), 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, std::array{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, // Unsigned Integer Tests ValidTestParam{ {Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE1), 0x01}, static_cast(1)}, ValidTestParam{{Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE2), 0x00, 0x01}, static_cast(1)}, ValidTestParam{{Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE4), 0x00, 0x00, 0x00, 0x01}, static_cast(1)}, ValidTestParam{{Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE8), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, static_cast(1)}, ValidTestParam{ {Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE16), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, std::array{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}, // UUID Tests ValidTestParam{ {Desc(DataElementType::UUID, DataElementSize::BYTE2), 0x01, 0x02}, Uuid::From16Bit(0x0102)}, ValidTestParam{{Desc(DataElementType::UUID, DataElementSize::BYTE4), 0x01, 0x02, 0x03, 0x04}, Uuid::From32Bit(0x01020304)}, ValidTestParam{ {Desc(DataElementType::UUID, DataElementSize::BYTE16), 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, Uuid::From128BitBE({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F})}, // String Tests ValidTestParam{ {Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_8BIT), 0x05, 'T', 'e', 's', 't', '1'}, std::string("Test1")}, ValidTestParam{ {Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_16BIT), 0x00, 0x05, 'T', 'e', 's', 't', '2'}, std::string("Test2")}, ValidTestParam{ {Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_32BIT), 0x00, 0x00, 0x00, 0x05, 'T', 'e', 's', 't', '3'}, std::string("Test3")}, // Nested Data Element List Tests ValidTestParam{ {Desc(DataElementType::DATA_ELEMENT_SEQUENCE, DataElementSize::ADDITIONAL_8BIT), 0x04, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00}, CreateReader( {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00})}, ValidTestParam{ {Desc(DataElementType::DATA_ELEMENT_SEQUENCE, DataElementSize::ADDITIONAL_16BIT), 0x00, 0x04, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00}, CreateReader( {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00})}, ValidTestParam{ {Desc(DataElementType::DATA_ELEMENT_SEQUENCE, DataElementSize::ADDITIONAL_32BIT), 0x00, 0x00, 0x00, 0x04, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00}, CreateReader( {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00})}, }; INSTANTIATE_TEST_CASE_P(ReadNext, ValidReadTest, ValuesIn(valid_values)); TEST_P(ValidReadTest, Test) { auto packet = ReaderPacket::Make(std::get<0>(GetParam())); auto value = std::get<1>(GetParam()); DataElementReader reader(packet->begin(), packet->end()); auto read_value = reader.ReadNext(); ASSERT_EQ(value, read_value); // Test that there is no additional data to read. ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate())); } // Test that a nested reader is correctly bounded and can't read past its // defined end. TEST(ReadNext, BoundedSubreaderTest) { std::vector payload = { // Subsequence descriptor byte. Desc(DataElementType::DATA_ELEMENT_SEQUENCE, DataElementSize::ADDITIONAL_8BIT), // Subsequence length. 0x04, // Subsequence that contains two booleans with values true and false. Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00, // Additional int16 at the end of the original sequence. Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE2), 0x01, 0x23}; auto packet = ReaderPacket::Make(payload); DataElementReader reader(packet->begin(), packet->end()); // The first thing read should be the subsequence. auto data_element = reader.ReadNext(); ASSERT_TRUE(std::holds_alternative(data_element)); // Check that the subsequence matches the premade sequence. auto subreader = std::get(data_element); data_element = subreader.ReadNext(); ASSERT_TRUE(std::holds_alternative(data_element)); ASSERT_TRUE(std::get(data_element)); data_element = subreader.ReadNext(); ASSERT_TRUE(std::holds_alternative(data_element)); ASSERT_FALSE(std::get(data_element)); // Check that there is no additional data to be read from the subreader. ASSERT_EQ(subreader.ReadNext(), DataElement(std::monostate())); // Check that we can still read the int16 from the original reader. data_element = reader.ReadNext(); ASSERT_TRUE(std::holds_alternative(data_element)); auto int16_value = std::get(data_element); ASSERT_EQ(int16_value, 0x0123); // Check that there is no additional data to be read from the base reader. ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate())); } // Test that trying to read an empty packet fails. TEST(ReadNext, NoDataTest) { auto packet = ReaderPacket::Make({}); DataElementReader reader(packet->begin(), packet->end()); ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate())); } // Test that using a reserved value for type fails. TEST(ReadNext, InvalidTypeTest) { auto packet = ReaderPacket::Make({0xFF}); DataElementReader reader(packet->begin(), packet->end()); ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate())); } // Test all invalid parses due to incorrect lengths or invalid sizes. All tests // should return std::monostate. using InvalidTestParam = std::vector; class InvalidReadTest : public TestWithParam {}; std::vector invalid_values = { // Boolean Tests: // Invalid size field. InvalidTestParam{ Desc(DataElementType::BOOLEAN, DataElementSize::BYTE2), }, // Insufficient data. InvalidTestParam{Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1)}, // Signed Integer Tests: // Invalid size field. InvalidTestParam{ Desc(DataElementType::SIGNED_INT, DataElementSize::ADDITIONAL_8BIT)}, // 1 byte insufficient data. InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE1)}, // 2 byte insufficient data. InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE2), 0x00}, // 4 byte insufficient data. InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE4), 0x00, 0x00, 0x00}, // 8 Byte insufficient data. InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE8), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 16 Byte insufficient data. InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE16), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Unsigned Integer Tests: // Invalid size field. InvalidTestParam{ Desc(DataElementType::UNSIGNED_INT, DataElementSize::ADDITIONAL_8BIT)}, // 1 byte insufficient data. InvalidTestParam{ Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE1)}, // 2 byte insufficient data. InvalidTestParam{ Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE2), 0x00}, // 4 byte insufficient data. InvalidTestParam{ Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE4), 0x00, 0x00, 0x00}, // 8 Byte insufficient data. InvalidTestParam{ Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE8), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 16 Byte insufficient data. InvalidTestParam{ Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE16), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // UUID Tests: // Invalid size field. InvalidTestParam{ Desc(DataElementType::UUID, DataElementSize::ADDITIONAL_8BIT)}, // 2 byte insufficient data. InvalidTestParam{Desc(DataElementType::UUID, DataElementSize::BYTE2), 0x00}, // 4 byte insufficient data. InvalidTestParam{Desc(DataElementType::UUID, DataElementSize::BYTE4), 0x00, 0x00, 0x00}, // 16 Byte insufficient data. InvalidTestParam{Desc(DataElementType::UUID, DataElementSize::BYTE16), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // String Tests: // Invalid size field. InvalidTestParam{Desc(DataElementType::STRING, DataElementSize::BYTE1)}, // Insufficient data for additional 8 bits len. InvalidTestParam{ Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_8BIT)}, // Insufficient data for additional 16 bits len. InvalidTestParam{ Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_16BIT), 0x00, }, // Insufficient data for additional 32 bit len. InvalidTestParam{ Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_32BIT), 0x00, 0x00, 0x00, }, // Insufficient data for reported length. InvalidTestParam{ Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_8BIT), 0x04, '1', '2', '3'}, // Nested Data Element List Tests: // Invalid size field. InvalidTestParam{ Desc(DataElementType::DATA_ELEMENT_SEQUENCE, DataElementSize::BYTE1)}, // Insufficient data for additional 8 bits len. InvalidTestParam{Desc(DataElementType::DATA_ELEMENT_SEQUENCE, DataElementSize::ADDITIONAL_8BIT)}, // Insufficient data for additional 16 bits len. InvalidTestParam{ Desc(DataElementType::DATA_ELEMENT_SEQUENCE, DataElementSize::ADDITIONAL_16BIT), 0x00, }, // Insufficient data for additional 32 bit len. InvalidTestParam{ Desc(DataElementType::DATA_ELEMENT_SEQUENCE, DataElementSize::ADDITIONAL_32BIT), 0x00, 0x00, 0x00, }, // Insufficient data for reported length. InvalidTestParam{Desc(DataElementType::DATA_ELEMENT_SEQUENCE, DataElementSize::ADDITIONAL_8BIT), 0x04, 0x00, 0x00, 0x00}, // Unhandled Data Element Types Tests: // NOTE: These tests should go away as we begin to handle the types. // Nil Type. InvalidTestParam{Desc(DataElementType::NIL, DataElementSize::BYTE1)}, // Data Element Alternative List Type. InvalidTestParam{Desc(DataElementType::DATA_ELEMENT_ALTERNATIVE, DataElementSize::ADDITIONAL_8BIT), 0x00}, // URL Type. InvalidTestParam{ Desc(DataElementType::URL, DataElementSize::ADDITIONAL_8BIT), 0x00}}; INSTANTIATE_TEST_CASE_P(ReadNext, InvalidReadTest, ValuesIn(invalid_values)); TEST_P(InvalidReadTest, Test) { auto packet = ReaderPacket::Make(GetParam()); DataElementReader reader(packet->begin(), packet->end()); ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate())); } // Test that trying to read from a reader with start > end crashes. TEST(DataElementReader, BadBoundsDeathTest) { auto packet = ReaderPacket::Make({0x00, 0x00, 0x00, 0x00}); DataElementReader reader(packet->end(), packet->begin()); ASSERT_DEATH(reader.ReadNext(), "Beginning of buffer is past end of buffer."); } } // namespace sdp } // namespace bluetooth