| /* |
| * Copyright 2020 HIMSA II K/S - www.himsa.com. |
| * Represented by EHIMA - www.ehima.com |
| * |
| * 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 "le_audio_types.h" |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include <vector> |
| |
| namespace bluetooth::le_audio { |
| namespace types { |
| |
| using ::testing::AllOf; |
| using ::testing::Contains; |
| using ::testing::ElementsAre; |
| using ::testing::ElementsAreArray; |
| using ::testing::Eq; |
| using ::testing::Pair; |
| using ::testing::SizeIs; |
| |
| TEST(LeAudioLtvMapTest, test_serialization) { |
| // clang-format off |
| const std::vector<uint8_t> ltv_test_vec{ |
| 0x02, 0x01, 0x0a, |
| 0x03, 0x02, 0xaa, 0xbb, |
| 0x04, 0x03, 0xde, 0xc0, 0xd0, |
| }; |
| |
| const std::vector<uint8_t> ltv_test_vec2{ |
| 0x04, 0x03, 0xde, 0xc0, 0xde, |
| 0x05, 0x04, 0xc0, 0xde, 0xc0, 0xde, |
| }; |
| |
| const std::vector<uint8_t> ltv_test_vec_expected{ |
| 0x02, 0x01, 0x0a, |
| 0x03, 0x02, 0xaa, 0xbb, |
| 0x04, 0x03, 0xde, 0xc0, 0xde, |
| 0x05, 0x04, 0xc0, 0xde, 0xc0, 0xde, |
| }; |
| // clang-format on |
| |
| // Parse |
| bool success; |
| LeAudioLtvMap ltv_map = |
| LeAudioLtvMap::Parse(ltv_test_vec.data(), ltv_test_vec.size(), success); |
| auto hash_one = ltv_map.GetHash(); |
| ASSERT_TRUE(success); |
| ASSERT_NE(hash_one, 0lu); |
| ASSERT_FALSE(ltv_map.IsEmpty()); |
| ASSERT_EQ((size_t)3, ltv_map.Size()); |
| |
| ASSERT_TRUE(ltv_map.Find(0x03)); |
| ASSERT_THAT(*(ltv_map.Find(0x03)), ElementsAre(0xde, 0xc0, 0xd0)); |
| |
| LeAudioLtvMap ltv_map2 = |
| LeAudioLtvMap::Parse(ltv_test_vec2.data(), ltv_test_vec2.size(), success); |
| auto hash_two = ltv_map2.GetHash(); |
| ASSERT_TRUE(success); |
| ASSERT_NE(hash_two, 0lu); |
| ASSERT_FALSE(ltv_map2.IsEmpty()); |
| ASSERT_EQ((size_t)2, ltv_map2.Size()); |
| ASSERT_NE(hash_one, hash_two); |
| |
| ltv_map.Append(ltv_map2); |
| ASSERT_NE(ltv_map.GetHash(), 0lu); |
| ASSERT_NE(ltv_map.GetHash(), hash_one); |
| ASSERT_NE(ltv_map.GetHash(), hash_two); |
| ASSERT_EQ((size_t)4, ltv_map.Size()); |
| |
| ASSERT_TRUE(ltv_map.Find(0x01)); |
| ASSERT_THAT(*(ltv_map.Find(0x01)), ElementsAre(0x0a)); |
| ASSERT_TRUE(ltv_map.Find(0x02)); |
| ASSERT_THAT(*(ltv_map.Find(0x02)), ElementsAre(0xaa, 0xbb)); |
| ASSERT_TRUE(ltv_map.Find(0x03)); |
| ASSERT_THAT(*(ltv_map.Find(0x03)), ElementsAre(0xde, 0xc0, 0xde)); |
| ASSERT_TRUE(ltv_map.Find(0x04)); |
| ASSERT_THAT(*(ltv_map.Find(0x04)), ElementsAre(0xc0, 0xde, 0xc0, 0xde)); |
| |
| // RawPacket |
| std::vector<uint8_t> serialized(ltv_map.RawPacketSize()); |
| ASSERT_TRUE(ltv_map.RawPacket(serialized.data())); |
| ASSERT_THAT(serialized, ElementsAreArray(ltv_test_vec_expected)); |
| ASSERT_THAT(ltv_map2.RawPacket(), ElementsAreArray(ltv_test_vec2)); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_serialization_macros) { |
| uint64_t value = 0x08090A0B0C0D0E0F; |
| |
| auto u16vec = UINT16_TO_VEC_UINT8(value); |
| ASSERT_EQ(sizeof(uint16_t), u16vec.size()); |
| ASSERT_EQ(0x0F, u16vec[0]); |
| ASSERT_EQ(0x0E, u16vec[1]); |
| |
| auto u32vec = UINT32_TO_VEC_UINT8(value); |
| ASSERT_EQ(sizeof(uint32_t), u32vec.size()); |
| ASSERT_EQ(0x0F, u32vec[0]); |
| ASSERT_EQ(0x0E, u32vec[1]); |
| ASSERT_EQ(0x0D, u32vec[2]); |
| ASSERT_EQ(0x0C, u32vec[3]); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_serialization_ltv_len_is_zero) { |
| // clang-format off |
| const std::vector<uint8_t> ltv_test_vec{ |
| 0x02, 0x01, 0x0a, |
| 0x03, 0x02, 0xaa, 0xbb, |
| 0x00, 0x00, 0x00, 0x00, 0x00, // ltv_len == 0 |
| 0x05, 0x04, 0xc0, 0xde, 0xc0, 0xde, |
| }; |
| // clang-format on |
| |
| // Parse |
| bool success; |
| LeAudioLtvMap ltv_map = |
| LeAudioLtvMap::Parse(ltv_test_vec.data(), ltv_test_vec.size(), success); |
| ASSERT_TRUE(success); |
| ASSERT_FALSE(ltv_map.IsEmpty()); |
| ASSERT_EQ((size_t)3, ltv_map.Size()); |
| |
| ASSERT_TRUE(ltv_map.Find(0x01)); |
| ASSERT_THAT(*(ltv_map.Find(0x01)), ElementsAre(0x0a)); |
| ASSERT_TRUE(ltv_map.Find(0x02)); |
| ASSERT_THAT(*(ltv_map.Find(0x02)), ElementsAre(0xaa, 0xbb)); |
| ASSERT_TRUE(ltv_map.Find(0x04)); |
| ASSERT_THAT(*(ltv_map.Find(0x04)), ElementsAre(0xc0, 0xde, 0xc0, 0xde)); |
| |
| // RawPacket |
| std::vector<uint8_t> serialized(ltv_map.RawPacketSize()); |
| ASSERT_TRUE(ltv_map.RawPacket(serialized.data())); |
| ASSERT_THAT(serialized, ElementsAre(0x02, 0x01, 0x0a, 0x03, 0x02, 0xaa, 0xbb, |
| 0x05, 0x04, 0xc0, 0xde, 0xc0, 0xde)); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_serialization_ltv_len_is_one) { |
| // clang-format off |
| const std::vector<uint8_t> ltv_test_vec{ |
| 0x02, 0x01, 0x0a, |
| 0x01, 0x02, |
| }; |
| // clang-format on |
| |
| // Parse |
| bool success; |
| LeAudioLtvMap ltv_map = |
| LeAudioLtvMap::Parse(ltv_test_vec.data(), ltv_test_vec.size(), success); |
| ASSERT_TRUE(success); |
| ASSERT_FALSE(ltv_map.IsEmpty()); |
| ASSERT_EQ((size_t)2, ltv_map.Size()); |
| |
| ASSERT_TRUE(ltv_map.Find(0x01)); |
| ASSERT_THAT(*(ltv_map.Find(0x01)), ElementsAre(0x0a)); |
| ASSERT_TRUE(ltv_map.Find(0x02)); |
| ASSERT_THAT(*(ltv_map.Find(0x02)), SizeIs(0)); |
| |
| // RawPacket |
| std::vector<uint8_t> serialized(ltv_map.RawPacketSize()); |
| ASSERT_TRUE(ltv_map.RawPacket(serialized.data())); |
| ASSERT_THAT(serialized, ElementsAreArray(ltv_test_vec)); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_serialization_ltv_len_is_invalid) { |
| // clang-format off |
| const std::vector<uint8_t> ltv_test_vec_1{ |
| 0x02, 0x01, 0x0a, |
| 0x04, 0x02, 0xaa, 0xbb, // one byte missing |
| }; |
| const std::vector<uint8_t> ltv_test_vec_2{ |
| 0x02, 0x01, 0x0a, |
| 0x03, 0x02, 0xaa, 0xbb, |
| 0x01, |
| }; |
| const std::vector<uint8_t> ltv_test_vec_3{ |
| 0x02, 0x01, 0x0a, |
| 0x03, 0x02, 0xaa, 0xbb, |
| 0x02, 0x03, |
| }; |
| // clang-format on |
| |
| // Parse |
| bool success = true; |
| LeAudioLtvMap ltv_map; |
| |
| ltv_map = LeAudioLtvMap::Parse(ltv_test_vec_1.data(), ltv_test_vec_1.size(), |
| success); |
| ASSERT_FALSE(success); |
| |
| ltv_map = LeAudioLtvMap::Parse(ltv_test_vec_2.data(), ltv_test_vec_2.size(), |
| success); |
| ASSERT_FALSE(success); |
| |
| ltv_map = LeAudioLtvMap::Parse(ltv_test_vec_3.data(), ltv_test_vec_3.size(), |
| success); |
| ASSERT_FALSE(success); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_configuration_valid) { |
| // clang-format off |
| const std::vector<uint8_t> config_ltv_vec{ |
| // SamplingFreq = 48000 |
| 0x02, 0x01, 0x08, |
| // FrameDuration = 10000us |
| 0x02, 0x02, 0x01, |
| // AudioChannelAllocation = kLeAudioLocationFrontLeft | |
| // kLeAudioLocationFrontRight |
| 0x05, 0x03, 0x03, 0x00, 0x00, 0x00, |
| // OctetsPerCodecFrame = 40 |
| 0x03, 0x04, 40, 0x00, |
| // Unknown type entry to ignore |
| 0x05, 0x06, 0x11, 0x22, 0x33, 0x44, |
| // CodecFrameBlocksPerSdu = 1 |
| 0x02, 0x05, 1, |
| }; |
| // clang-format on |
| |
| // Parse |
| bool success = true; |
| LeAudioLtvMap ltv_map = LeAudioLtvMap::Parse(config_ltv_vec.data(), |
| config_ltv_vec.size(), success); |
| ASSERT_TRUE(success); |
| |
| // Verify the codec configuration values |
| auto config = ltv_map.GetAsCoreCodecConfig(); |
| |
| // SamplingFreq = 48000 |
| ASSERT_TRUE(config.sampling_frequency.has_value()); |
| ASSERT_EQ(0x08, config.sampling_frequency.value()); |
| ASSERT_EQ(48000u, config.GetSamplingFrequencyHz()); |
| |
| // FrameDuration = 10000us |
| ASSERT_TRUE(config.frame_duration.has_value()); |
| ASSERT_EQ(0x01, config.frame_duration.value()); |
| ASSERT_EQ(10000u, config.GetFrameDurationUs()); |
| |
| // AudioChannelAllocation = kLeAudioLocationFrontLeft | |
| // kLeAudioLocationFrontRight |
| ASSERT_TRUE(config.audio_channel_allocation.has_value()); |
| ASSERT_EQ(0x00000003u, config.audio_channel_allocation.value()); |
| |
| // OctetsPerCodecFrame = 40 |
| ASSERT_TRUE(config.octets_per_codec_frame.has_value()); |
| ASSERT_EQ(0x0028u, config.octets_per_codec_frame.value()); |
| |
| // CodecFrameBlocksPerSdu = 1 |
| ASSERT_TRUE(config.codec_frames_blocks_per_sdu.has_value()); |
| ASSERT_EQ(0x01u, config.codec_frames_blocks_per_sdu.value()); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_capabilities_valid) { |
| // clang-format off |
| const std::vector<uint8_t> capabilities_ltv_vec{ |
| // SupportedSamplingFrequencies = 96000 and 16000 |
| 0x03, 0x01, |
| (uint8_t)(codec_spec_caps::kLeAudioSamplingFreq16000Hz) | |
| (uint8_t)(codec_spec_caps::kLeAudioSamplingFreq96000Hz), |
| (uint8_t)(codec_spec_caps::kLeAudioSamplingFreq16000Hz >> 8) | |
| (uint8_t)(codec_spec_caps::kLeAudioSamplingFreq96000Hz >> 8), |
| // SupportedFrameDurations = 10ms, 7.5ms, 10ms preferred |
| 0x02, 0x02, codec_spec_caps::kLeAudioCodecFrameDur7500us | |
| codec_spec_caps::kLeAudioCodecFrameDur10000us | |
| codec_spec_caps::kLeAudioCodecFrameDurPrefer10000us, |
| // SupportedAudioChannelCounts = 0b1 | 0b2 (one and two channels) |
| 0x02, 0x03, 0b01 | 0b10, |
| // SupportedOctetsPerCodecFrame = min:40, max:80 |
| 0x05, 0x04, 40, 00, 80, 00, |
| // Unknown type entry to ignore |
| 0x05, 0x06, 0x11, 0x22, 0x33, 0x44, |
| // SupportedMaxCodecFramesPerSdu = 2 |
| 0x02, 0x05, 0x02, |
| }; |
| // clang-format on |
| |
| // Parse |
| bool success = true; |
| LeAudioLtvMap ltv_map = LeAudioLtvMap::Parse( |
| capabilities_ltv_vec.data(), capabilities_ltv_vec.size(), success); |
| ASSERT_TRUE(success); |
| |
| // Verify the codec capabilities values |
| auto caps = ltv_map.GetAsCoreCodecCapabilities(); |
| |
| // SupportedSamplingFrequencies = 96000 and 16000 |
| ASSERT_TRUE(caps.HasSupportedSamplingFrequencies()); |
| ASSERT_EQ(codec_spec_caps::kLeAudioSamplingFreq16000Hz | |
| codec_spec_caps::kLeAudioSamplingFreq96000Hz, |
| caps.supported_sampling_frequencies.value()); |
| // Check config values agains the capabilities |
| ASSERT_FALSE(caps.IsSamplingFrequencyConfigSupported( |
| codec_spec_conf::kLeAudioSamplingFreq8000Hz)); |
| ASSERT_FALSE(caps.IsSamplingFrequencyConfigSupported( |
| codec_spec_conf::kLeAudioSamplingFreq11025Hz)); |
| ASSERT_TRUE(caps.IsSamplingFrequencyConfigSupported( |
| codec_spec_conf::kLeAudioSamplingFreq16000Hz)); |
| ASSERT_FALSE(caps.IsSamplingFrequencyConfigSupported( |
| codec_spec_conf::kLeAudioSamplingFreq22050Hz)); |
| ASSERT_FALSE(caps.IsSamplingFrequencyConfigSupported( |
| codec_spec_conf::kLeAudioSamplingFreq24000Hz)); |
| ASSERT_FALSE(caps.IsSamplingFrequencyConfigSupported( |
| codec_spec_conf::kLeAudioSamplingFreq32000Hz)); |
| ASSERT_FALSE(caps.IsSamplingFrequencyConfigSupported( |
| codec_spec_conf::kLeAudioSamplingFreq44100Hz)); |
| ASSERT_FALSE(caps.IsSamplingFrequencyConfigSupported( |
| codec_spec_conf::kLeAudioSamplingFreq48000Hz)); |
| ASSERT_FALSE(caps.IsSamplingFrequencyConfigSupported( |
| codec_spec_conf::kLeAudioSamplingFreq88200Hz)); |
| ASSERT_TRUE(caps.IsSamplingFrequencyConfigSupported( |
| codec_spec_conf::kLeAudioSamplingFreq96000Hz)); |
| ASSERT_FALSE(caps.IsSamplingFrequencyConfigSupported( |
| codec_spec_conf::kLeAudioSamplingFreq176400Hz)); |
| ASSERT_FALSE(caps.IsSamplingFrequencyConfigSupported( |
| codec_spec_conf::kLeAudioSamplingFreq192000Hz)); |
| ASSERT_FALSE(caps.IsSamplingFrequencyConfigSupported( |
| codec_spec_conf::kLeAudioSamplingFreq384000Hz)); |
| |
| // SupportedFrameDurations = 10ms, 7.5ms, 10ms preferred |
| ASSERT_TRUE(caps.HasSupportedFrameDurations()); |
| ASSERT_EQ(codec_spec_caps::kLeAudioCodecFrameDur7500us | |
| codec_spec_caps::kLeAudioCodecFrameDur10000us | |
| codec_spec_caps::kLeAudioCodecFrameDurPrefer10000us, |
| caps.supported_frame_durations.value()); |
| // Check config values agains the capabilities |
| ASSERT_TRUE(caps.IsFrameDurationConfigSupported( |
| codec_spec_conf::kLeAudioCodecFrameDur7500us)); |
| ASSERT_TRUE(caps.IsFrameDurationConfigSupported( |
| codec_spec_conf::kLeAudioCodecFrameDur10000us)); |
| |
| // SupportedAudioChannelCounts = 0b1 | 0b2 (one and two channels) |
| ASSERT_TRUE(caps.HasSupportedAudioChannelCounts()); |
| ASSERT_EQ(codec_spec_caps::kLeAudioCodecChannelCountSingleChannel | |
| codec_spec_caps::kLeAudioCodecChannelCountTwoChannel, |
| caps.supported_audio_channel_counts.value()); |
| // Check config values agains the capabilities |
| ASSERT_TRUE(caps.IsAudioChannelCountsSupported(1)); |
| ASSERT_TRUE(caps.IsAudioChannelCountsSupported(2)); |
| for (uint8_t i = 3; i < 8; ++i) { |
| ASSERT_FALSE(caps.IsAudioChannelCountsSupported(i)); |
| } |
| |
| // SupportedOctetsPerCodecFrame = min:40, max:80 |
| ASSERT_TRUE(caps.HasSupportedOctetsPerCodecFrame()); |
| ASSERT_EQ(codec_spec_caps::kLeAudioCodecFrameLen40, |
| caps.supported_min_octets_per_codec_frame.value()); |
| ASSERT_EQ(codec_spec_caps::kLeAudioCodecFrameLen80, |
| caps.supported_max_octets_per_codec_frame.value()); |
| // Check config values agains the capabilities |
| ASSERT_FALSE(caps.IsOctetsPerCodecFrameConfigSupported( |
| codec_spec_conf::kLeAudioCodecFrameLen30)); |
| ASSERT_TRUE(caps.IsOctetsPerCodecFrameConfigSupported( |
| codec_spec_conf::kLeAudioCodecFrameLen40)); |
| // Supported since: 40(min) < 60 < 80(max) |
| ASSERT_TRUE(caps.IsOctetsPerCodecFrameConfigSupported( |
| codec_spec_conf::kLeAudioCodecFrameLen60)); |
| ASSERT_TRUE(caps.IsOctetsPerCodecFrameConfigSupported( |
| codec_spec_conf::kLeAudioCodecFrameLen80)); |
| ASSERT_FALSE(caps.IsOctetsPerCodecFrameConfigSupported( |
| codec_spec_conf::kLeAudioCodecFrameLen120)); |
| |
| // SupportedMaxCodecFramesPerSdu = 2 |
| ASSERT_TRUE(caps.HasSupportedMaxCodecFramesPerSdu()); |
| ASSERT_EQ(2, caps.supported_max_codec_frames_per_sdu.value()); |
| // Check config values agains the capabilities: {1,2} <= 2(max) |
| ASSERT_TRUE(caps.IsCodecFramesPerSduSupported(1)); |
| ASSERT_TRUE(caps.IsCodecFramesPerSduSupported(2)); |
| ASSERT_FALSE(caps.IsCodecFramesPerSduSupported(3)); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_metadata_use_guard1) { |
| auto default_context = |
| (uint16_t)bluetooth::le_audio::types::LeAudioContextType::VOICEASSISTANTS; |
| static const std::vector<uint8_t> default_metadata = { |
| bluetooth::le_audio::types::kLeAudioMetadataStreamingAudioContextLen + 1, |
| bluetooth::le_audio::types::kLeAudioMetadataTypeStreamingAudioContext, |
| (uint8_t)(default_context & 0x00FF), |
| (uint8_t)((default_context & 0xFF00) >> 8)}; |
| |
| // Parse |
| bool success = true; |
| LeAudioLtvMap ltv_map = LeAudioLtvMap::Parse( |
| default_metadata.data(), default_metadata.size(), success); |
| ASSERT_TRUE(success); |
| |
| // Verify the codec capabilities values |
| auto metadata = ltv_map.GetAsLeAudioMetadata(); |
| |
| // Should fail when trying to reinterpret the LTV as configuration |
| EXPECT_DEATH(ltv_map.GetAsCoreCodecConfig(), ""); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_metadata_use_guard2) { |
| auto default_context = |
| (uint16_t)bluetooth::le_audio::types::LeAudioContextType::VOICEASSISTANTS; |
| static const std::vector<uint8_t> default_metadata = { |
| bluetooth::le_audio::types::kLeAudioMetadataStreamingAudioContextLen + 1, |
| bluetooth::le_audio::types::kLeAudioMetadataTypeStreamingAudioContext, |
| (uint8_t)(default_context & 0x00FF), |
| (uint8_t)((default_context & 0xFF00) >> 8)}; |
| |
| // Parse |
| bool success = true; |
| LeAudioLtvMap ltv_map = LeAudioLtvMap::Parse( |
| default_metadata.data(), default_metadata.size(), success); |
| ASSERT_TRUE(success); |
| |
| // Verify the codec capabilities values |
| auto metadata = ltv_map.GetAsLeAudioMetadata(); |
| |
| // Should fail when trying to reinterpret the LTV as configuration |
| EXPECT_DEATH(ltv_map.GetAsCoreCodecCapabilities(), ""); |
| } |
| |
| static auto PrepareMetadataLtv() { |
| ::bluetooth::le_audio::types::LeAudioLtvMap metadata_ltvs; |
| // Prepare the metadata LTVs |
| metadata_ltvs |
| .Add(::bluetooth::le_audio::types:: |
| kLeAudioMetadataTypePreferredAudioContext, |
| (uint16_t)10) |
| .Add(::bluetooth::le_audio::types:: |
| kLeAudioMetadataTypeStreamingAudioContext, |
| (uint16_t)8) |
| .Add(::bluetooth::le_audio::types::kLeAudioMetadataTypeProgramInfo, |
| std::string{"ProgramInfo"}) |
| .Add(::bluetooth::le_audio::types::kLeAudioMetadataTypeLanguage, |
| std::string{"ice"}) |
| .Add(::bluetooth::le_audio::types::kLeAudioMetadataTypeCcidList, |
| std::vector<uint8_t>{1, 2, 3}) |
| .Add(::bluetooth::le_audio::types::kLeAudioMetadataTypeparentalRating, |
| (uint8_t)0x01) |
| .Add(::bluetooth::le_audio::types::kLeAudioMetadataTypeProgramInfoUri, |
| std::string{"ProgramInfoUri"}) |
| .Add(::bluetooth::le_audio::types::kLeAudioMetadataTypeAudioActiveState, |
| false) |
| .Add(::bluetooth::le_audio::types:: |
| kLeAudioMetadataTypeBroadcastAudioImmediateRenderingFlag, |
| true) |
| .Add(::bluetooth::le_audio::types::kLeAudioMetadataTypeExtendedMetadata, |
| std::vector<uint8_t>{1, 2, 3}) |
| .Add(::bluetooth::le_audio::types::kLeAudioMetadataTypeVendorSpecific, |
| std::vector<uint8_t>{1, 2, 3}); |
| return metadata_ltvs; |
| } |
| |
| TEST(LeAudioLtvMapTest, test_metadata_valid) { |
| // Prepare the reference LTV |
| auto metadata_ltv = PrepareMetadataLtv(); |
| auto raw_metadata = metadata_ltv.RawPacket(); |
| |
| // Check the Parsing |
| bool success = true; |
| LeAudioLtvMap parsed_ltv_map = |
| LeAudioLtvMap::Parse(raw_metadata.data(), raw_metadata.size(), success); |
| ASSERT_TRUE(success); |
| |
| // Verify the values |
| auto metadata = metadata_ltv.GetAsLeAudioMetadata(); |
| auto parsed_metadata = parsed_ltv_map.GetAsLeAudioMetadata(); |
| ASSERT_EQ(parsed_metadata.preferred_audio_context.value(), |
| metadata.preferred_audio_context.value()); |
| ASSERT_EQ(parsed_metadata.program_info.value(), |
| metadata.program_info.value()); |
| ASSERT_TRUE(parsed_metadata.language.has_value()); |
| ASSERT_TRUE(metadata.language.has_value()); |
| ASSERT_EQ(parsed_metadata.language.value(), metadata.language.value()); |
| ASSERT_EQ(parsed_metadata.ccid_list.value(), metadata.ccid_list.value()); |
| ASSERT_EQ(parsed_metadata.parental_rating.value(), |
| metadata.parental_rating.value()); |
| ASSERT_EQ(parsed_metadata.program_info_uri.value(), |
| metadata.program_info_uri.value()); |
| ASSERT_EQ(parsed_metadata.audio_active_state.value(), |
| metadata.audio_active_state.value()); |
| ASSERT_EQ(parsed_metadata.broadcast_audio_immediate_rendering.value(), |
| metadata.broadcast_audio_immediate_rendering.value()); |
| ASSERT_EQ(parsed_metadata.extended_metadata.value(), |
| metadata.extended_metadata.value()); |
| ASSERT_EQ(parsed_metadata.vendor_specific.value(), |
| metadata.vendor_specific.value()); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_adding_types) { |
| LeAudioLtvMap ltv_map; |
| ltv_map.Add(1, (uint8_t)127); |
| ltv_map.Add(2, (uint16_t)32767); |
| ltv_map.Add(3, (uint32_t)65535); |
| ltv_map.Add(4, std::vector<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8, 9}); |
| ltv_map.Add(5, std::string("sample text")); |
| ltv_map.Add(6, true); |
| |
| ASSERT_EQ(6lu, ltv_map.Size()); |
| |
| uint8_t u8; |
| auto pp = ltv_map.At(1).data(); |
| STREAM_TO_UINT8(u8, pp); |
| ASSERT_EQ((uint8_t)127, u8); |
| |
| uint16_t u16; |
| pp = ltv_map.At(2).data(); |
| STREAM_TO_UINT16(u16, pp); |
| ASSERT_EQ((uint16_t)32767, u16); |
| |
| uint32_t u32; |
| pp = ltv_map.At(3).data(); |
| STREAM_TO_UINT32(u32, pp); |
| ASSERT_EQ((uint32_t)65535, u32); |
| |
| ASSERT_EQ((std::vector<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8, 9}), ltv_map.At(4)); |
| |
| ASSERT_EQ(std::string("sample text"), |
| std::string(ltv_map.At(5).begin(), ltv_map.At(5).end())); |
| |
| ASSERT_EQ(true, (bool)ltv_map.At(6).data()[0]); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_hash_sanity) { |
| LeAudioLtvMap ltv_map; |
| ASSERT_EQ(ltv_map.GetHash(), 0lu); |
| |
| auto hash = ltv_map.GetHash(); |
| ltv_map.Add(0, (uint8_t)127); |
| ltv_map.Add(1, (uint16_t)32767); |
| ltv_map.Add(2, (uint32_t)65535); |
| ltv_map.Add(3, std::vector<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8, 9}); |
| |
| ASSERT_NE(ltv_map.GetHash(), 0lu); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| ASSERT_EQ(ltv_map, ltv_map); |
| |
| // Compare hashes of equal LTV maps, filled in a different order |
| LeAudioLtvMap ltv_map_two; |
| ltv_map_two.Add(3, std::vector<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8, 9}); |
| ltv_map_two.Add(0, (uint8_t)127); |
| ltv_map_two.Add(2, (uint32_t)65535); |
| ltv_map_two.Add(1, (uint16_t)32767); |
| ASSERT_EQ(ltv_map, ltv_map_two); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_value_hash_sanity) { |
| LeAudioLtvMap ltv_map; |
| |
| ltv_map.Add(1, (uint16_t)32767); |
| auto hash = ltv_map.GetHash(); |
| |
| // Same value but type size is different |
| hash = ltv_map.GetHash(); |
| ltv_map.Add(1, (uint32_t)32767); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_type_change_same_value) { |
| LeAudioLtvMap ltv_map_one; |
| ltv_map_one.Add(1, (uint16_t)32767); |
| |
| LeAudioLtvMap ltv_map_two; |
| // The same value but different type |
| ltv_map_two.Add(3, (uint16_t)32767); |
| |
| ASSERT_NE(ltv_map_one.GetHash(), ltv_map_two.GetHash()); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_add_changing_hash) { |
| LeAudioLtvMap ltv_map; |
| |
| auto hash = ltv_map.GetHash(); |
| ltv_map.Add(0, (uint8_t)127); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.Add(1, (uint16_t)32767); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.Add(2, (uint32_t)65535); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.Add(3, std::vector<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8, 9}); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_update_changing_hash) { |
| LeAudioLtvMap ltv_map; |
| |
| auto hash = ltv_map.GetHash(); |
| ltv_map.Add(0, (uint8_t)127); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.Add(0, (uint16_t)32767); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.Add(0, (uint32_t)65535); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.Add(0, std::vector<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8, 9}); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_update_same_not_changing_hash) { |
| LeAudioLtvMap ltv_map; |
| |
| auto hash = ltv_map.GetHash(); |
| ltv_map.Add(0, (uint8_t)127); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.Add(0, (uint8_t)127); |
| ASSERT_EQ(ltv_map.GetHash(), hash); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_remove_changing_hash) { |
| LeAudioLtvMap ltv_map; |
| |
| auto hash = ltv_map.GetHash(); |
| ltv_map.Add(0, (uint8_t)127); |
| ltv_map.Add(1, (uint16_t)32767); |
| ltv_map.Add(2, (uint32_t)65535); |
| ltv_map.Add(3, std::vector<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8, 9}); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.Remove(0); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.Remove(1); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.Remove(2); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.Remove(3); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_clear_changing_hash) { |
| LeAudioLtvMap ltv_map; |
| |
| auto hash = ltv_map.GetHash(); |
| ltv_map.Add(0, (uint8_t)127); |
| ltv_map.Add(1, (uint16_t)32767); |
| ltv_map.Add(2, (uint32_t)65535); |
| ltv_map.Add(3, std::vector<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8, 9}); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.Clear(); |
| ASSERT_NE(ltv_map.GetHash(), hash); |
| |
| // 2nd clear should not change it |
| hash = ltv_map.GetHash(); |
| ltv_map.Clear(); |
| ASSERT_EQ(ltv_map.GetHash(), hash); |
| |
| // Check if empty maps have equal hash |
| LeAudioLtvMap empty_ltv_map; |
| ASSERT_EQ(empty_ltv_map, ltv_map); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_remove_all_changing_hash) { |
| LeAudioLtvMap ltv_map; |
| |
| auto hash = ltv_map.GetHash(); |
| ltv_map.Add(0, (uint8_t)127); |
| ltv_map.Add(1, (uint16_t)32767); |
| ltv_map.Add(2, (uint32_t)65535); |
| ltv_map.Add(3, std::vector<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8, 9}); |
| |
| LeAudioLtvMap ltv_map_1st_half; |
| ltv_map_1st_half.Add(1, (uint16_t)32767); |
| ltv_map_1st_half.Add(3, std::vector<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8, 9}); |
| |
| LeAudioLtvMap ltv_map_2nd_half; |
| ltv_map_2nd_half.Add(0, (uint8_t)127); |
| ltv_map_2nd_half.Add(2, (uint32_t)65535); |
| |
| ASSERT_NE(ltv_map_1st_half, ltv_map_2nd_half); |
| ASSERT_NE(ltv_map, ltv_map_2nd_half); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.RemoveAllTypes(ltv_map_1st_half); |
| ASSERT_NE(hash, ltv_map.GetHash()); |
| |
| hash = ltv_map.GetHash(); |
| ltv_map.RemoveAllTypes(ltv_map_2nd_half); |
| ASSERT_NE(hash, ltv_map.GetHash()); |
| |
| // Check if empty maps have equal hash |
| LeAudioLtvMap empty_ltv_map; |
| ASSERT_EQ(empty_ltv_map, ltv_map); |
| } |
| |
| TEST(LeAudioLtvMapTest, test_intersection) { |
| LeAudioLtvMap ltv_map_one; |
| ltv_map_one.Add(1, (uint16_t)32767); |
| ltv_map_one.Add(3, std::vector<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8, 9}); |
| ltv_map_one.Add(2, (uint32_t)65535); |
| |
| LeAudioLtvMap ltv_map_two; |
| ltv_map_two.Add(0, (uint8_t)127); |
| // Not the type is the same but value differs |
| ltv_map_two.Add(1, (uint16_t)32766); |
| ltv_map_two.Add(2, (uint32_t)65535); |
| |
| LeAudioLtvMap ltv_map_common; |
| ltv_map_common.Add(2, (uint32_t)65535); |
| ASSERT_NE(ltv_map_common.GetHash(), 0lu); |
| |
| ASSERT_EQ(ltv_map_one.GetIntersection(ltv_map_two).GetHash(), |
| ltv_map_common.GetHash()); |
| ASSERT_EQ(ltv_map_two.GetIntersection(ltv_map_one), ltv_map_common); |
| } |
| |
| constexpr types::LeAudioCodecId kLeAudioCodecIdVendor1 = { |
| .coding_format = types::kLeAudioCodingFormatVendorSpecific, |
| // Not a particualr vendor - just some random numbers |
| .vendor_company_id = 0xC0, |
| .vendor_codec_id = 0xDE, |
| }; |
| |
| static const set_configurations::CodecConfigSetting vendor_16_2 = { |
| .id = kLeAudioCodecIdVendor1, |
| .params = types::LeAudioLtvMap({ |
| LTV_ENTRY_SAMPLING_FREQUENCY( |
| codec_spec_conf::kLeAudioSamplingFreq16000Hz), |
| LTV_ENTRY_FRAME_DURATION(codec_spec_conf::kLeAudioCodecFrameDur10000us), |
| LTV_ENTRY_AUDIO_CHANNEL_ALLOCATION( |
| codec_spec_conf::kLeAudioLocationStereo), |
| LTV_ENTRY_OCTETS_PER_CODEC_FRAME(40), |
| }), |
| .vendor_params = {0x01, 0x02, 0x03, 0x04}, |
| .channel_count_per_iso_stream = 1, |
| }; |
| |
| TEST(CodecConfigSettingTest, test_vendor_codec_type) { |
| auto vendor_codec = vendor_16_2; |
| ASSERT_EQ(vendor_16_2, vendor_codec); |
| } |
| |
| } // namespace types |
| } // namespace bluetooth::le_audio |