/* * Copyright (C) 2021 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. */ //#define LOG_NDEBUG 0 #define LOG_TAG "AudioClientSerializationUnitTests" #include #include #include #include #include #include #include "audio_test_utils.h" using namespace android; namespace xsd { using namespace ::android::audio::policy::configuration::V7_0; } template std::vector getFlags(const xsdc_enum_range& range, const FUNC& func, const std::string& findString = {}) { std::vector vec; for (const auto& xsdEnumVal : range) { T enumVal; std::string enumString = toString(xsdEnumVal); if (enumString.find(findString) != std::string::npos && func(enumString.c_str(), &enumVal)) { vec.push_back(enumVal); } } return vec; } static const std::vector kUsages = getFlags( xsdc_enum_range{}, audio_usage_from_string); static const std::vector kContentType = getFlags(xsdc_enum_range{}, audio_content_type_from_string); static const std::vector kInputSources = getFlags( xsdc_enum_range{}, audio_source_from_string); static const std::vector kStreamtypes = getFlags(xsdc_enum_range{}, audio_stream_type_from_string); static const std::vector kMixMatchRules = {RULE_MATCH_ATTRIBUTE_USAGE, RULE_EXCLUDE_ATTRIBUTE_USAGE, RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET, RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET, RULE_MATCH_UID, RULE_EXCLUDE_UID, RULE_MATCH_USERID, RULE_EXCLUDE_USERID, RULE_MATCH_AUDIO_SESSION_ID, RULE_EXCLUDE_AUDIO_SESSION_ID}; // Generates a random string. std::string CreateRandomString(size_t n) { std::string data = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; srand(static_cast(time(0))); std::string s(n, ' '); for (size_t i = 0; i < n; ++i) { s[i] = data[rand() % data.size()]; } return s; } class FillAudioAttributes { public: void fillAudioAttributes(audio_attributes_t& attr); unsigned int mSeed; }; void FillAudioAttributes::fillAudioAttributes(audio_attributes_t& attr) { attr.content_type = kContentType[rand() % kContentType.size()]; attr.usage = kUsages[rand() % kUsages.size()]; attr.source = kInputSources[rand() % kInputSources.size()]; // attr.flags -> [0, (1 << (CAPTURE_PRIVATE + 1) - 1)] attr.flags = static_cast(rand() & 0x3fff); sprintf(attr.tags, "%s", CreateRandomString((int)rand() % (AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1)).c_str()); } class SerializationTest : public FillAudioAttributes, public ::testing::Test { void SetUp() override { mSeed = static_cast(time(0)); srand(mSeed); } }; // UNIT TESTS TEST_F(SerializationTest, AudioProductStrategyBinderization) { for (int j = 0; j < 512; j++) { const std::string name{"Test APSBinderization for seed::" + std::to_string(mSeed)}; std::vector volumeGroupAttrVector; for (auto i = 0; i < 16; i++) { audio_attributes_t attributes; fillAudioAttributes(attributes); VolumeGroupAttributes volumeGroupAttr{static_cast(rand()), kStreamtypes[rand() % kStreamtypes.size()], attributes}; volumeGroupAttrVector.push_back(volumeGroupAttr); } product_strategy_t psId = static_cast(rand()); AudioProductStrategy aps{name, volumeGroupAttrVector, psId}; Parcel p; EXPECT_EQ(NO_ERROR, aps.writeToParcel(&p)) << name; AudioProductStrategy apsCopy; p.setDataPosition(0); EXPECT_EQ(NO_ERROR, apsCopy.readFromParcel(&p)) << name; EXPECT_EQ(apsCopy.getName(), name) << name; EXPECT_EQ(apsCopy.getId(), psId) << name; auto avec = apsCopy.getVolumeGroupAttributes(); EXPECT_EQ(avec.size(), volumeGroupAttrVector.size()) << name; for (int i = 0; i < volumeGroupAttrVector.size(); i++) { EXPECT_EQ(avec[i].getGroupId(), volumeGroupAttrVector[i].getGroupId()) << name; EXPECT_EQ(avec[i].getStreamType(), volumeGroupAttrVector[i].getStreamType()) << name; EXPECT_TRUE(avec[i].getAttributes() == volumeGroupAttrVector[i].getAttributes()) << name; } } } TEST_F(SerializationTest, AudioVolumeGroupBinderization) { for (int j = 0; j < 512; j++) { const std::string name{"Test AVGBinderization for seed::" + std::to_string(mSeed)}; volume_group_t groupId = static_cast(rand()); std::vector attributesvector; for (auto i = 0; i < 16; i++) { audio_attributes_t attributes; fillAudioAttributes(attributes); attributesvector.push_back(attributes); } std::vector streamsvector; for (auto i = 0; i < 8; i++) { streamsvector.push_back(kStreamtypes[rand() % kStreamtypes.size()]); } AudioVolumeGroup avg{name, groupId, attributesvector, streamsvector}; Parcel p; EXPECT_EQ(NO_ERROR, avg.writeToParcel(&p)); AudioVolumeGroup avgCopy; p.setDataPosition(0); EXPECT_EQ(NO_ERROR, avgCopy.readFromParcel(&p)) << name; EXPECT_EQ(avgCopy.getName(), name) << name; EXPECT_EQ(avgCopy.getId(), groupId) << name; auto avec = avgCopy.getAudioAttributes(); EXPECT_EQ(avec.size(), attributesvector.size()) << name; for (int i = 0; i < avec.size(); i++) { EXPECT_TRUE(avec[i] == attributesvector[i]) << name; } StreamTypeVector svec = avgCopy.getStreamTypes(); EXPECT_EQ(svec.size(), streamsvector.size()) << name; for (int i = 0; i < svec.size(); i++) { EXPECT_EQ(svec[i], streamsvector[i]) << name; } } } TEST_F(SerializationTest, AudioMixBinderization) { for (int j = 0; j < 512; j++) { const std::string msg{"Test AMBinderization for seed::" + std::to_string(mSeed)}; std::vector criteria; criteria.reserve(16); for (int i = 0; i < 16; i++) { AudioMixMatchCriterion ammc{kUsages[rand() % kUsages.size()], kInputSources[rand() % kInputSources.size()], kMixMatchRules[rand() % kMixMatchRules.size()]}; criteria.push_back(ammc); } audio_config_t config{}; config.sample_rate = 48000; config.channel_mask = AUDIO_CHANNEL_IN_MONO; config.format = AUDIO_FORMAT_PCM_16_BIT; config.offload_info = AUDIO_INFO_INITIALIZER; config.frame_count = 4800; AudioMix am{criteria, static_cast(rand()), config, static_cast(rand()), String8(msg.c_str()), static_cast(rand())}; Parcel p; EXPECT_EQ(NO_ERROR, am.writeToParcel(&p)) << msg; AudioMix amCopy; p.setDataPosition(0); EXPECT_EQ(NO_ERROR, amCopy.readFromParcel(&p)) << msg; EXPECT_EQ(amCopy.mMixType, am.mMixType) << msg; EXPECT_EQ(amCopy.mFormat.sample_rate, am.mFormat.sample_rate) << msg; EXPECT_EQ(amCopy.mFormat.channel_mask, am.mFormat.channel_mask) << msg; EXPECT_EQ(amCopy.mFormat.format, am.mFormat.format) << msg; EXPECT_EQ(amCopy.mRouteFlags, am.mRouteFlags) << msg; EXPECT_EQ(amCopy.mDeviceAddress, am.mDeviceAddress) << msg; EXPECT_EQ(amCopy.mCbFlags, am.mCbFlags) << msg; EXPECT_EQ(amCopy.mCriteria.size(), am.mCriteria.size()) << msg; for (auto i = 0; i < amCopy.mCriteria.size(); i++) { EXPECT_EQ(amCopy.mCriteria[i].mRule, am.mCriteria[i].mRule) << msg; EXPECT_EQ(amCopy.mCriteria[i].mValue.mUserId, am.mCriteria[i].mValue.mUserId) << msg; } } } using MMCTestParams = std::tuple; class MMCParameterizedTest : public FillAudioAttributes, public ::testing::TestWithParam { public: MMCParameterizedTest() : mAudioUsage(std::get<0>(GetParam())), mAudioSource(std::get<1>(GetParam())), mAudioMixMatchRules(std::get<2>(GetParam())){}; const audio_usage_t mAudioUsage; const audio_source_t mAudioSource; const uint32_t mAudioMixMatchRules; void SetUp() override { mSeed = static_cast(time(0)); srand(mSeed); } }; TEST_P(MMCParameterizedTest, AudioMixMatchCriterionBinderization) { const std::string msg{"Test AMMCBinderization for seed::" + std::to_string(mSeed)}; AudioMixMatchCriterion ammc{mAudioUsage, mAudioSource, mAudioMixMatchRules}; Parcel p; EXPECT_EQ(NO_ERROR, ammc.writeToParcel(&p)) << msg; AudioMixMatchCriterion ammcCopy; p.setDataPosition(0); EXPECT_EQ(NO_ERROR, ammcCopy.readFromParcel(&p)) << msg; EXPECT_EQ(ammcCopy.mRule, ammc.mRule) << msg; EXPECT_EQ(ammcCopy.mValue.mUserId, ammc.mValue.mUserId) << msg; } // audioUsage, audioSource, audioMixMatchRules INSTANTIATE_TEST_SUITE_P(SerializationParameterizedTests, MMCParameterizedTest, ::testing::Combine(testing::ValuesIn(kUsages), testing::ValuesIn(kInputSources), testing::ValuesIn(kMixMatchRules))); using AudioAttributesTestParams = std::tuple; class AudioAttributesParameterizedTest : public FillAudioAttributes, public ::testing::TestWithParam { public: AudioAttributesParameterizedTest() : mAudioStream(std::get<0>(GetParam())){}; const audio_stream_type_t mAudioStream; void SetUp() override { mSeed = static_cast(time(0)); srand(mSeed); } }; TEST_P(AudioAttributesParameterizedTest, AudioAttributesBinderization) { const std::string msg{"Test AABinderization for seed::" + std::to_string(mSeed)}; volume_group_t groupId = static_cast(rand()); audio_stream_type_t stream = mAudioStream; audio_attributes_t attributes; fillAudioAttributes(attributes); VolumeGroupAttributes volumeGroupAttr{groupId, stream, attributes}; Parcel p; EXPECT_EQ(NO_ERROR, volumeGroupAttr.writeToParcel(&p)) << msg; VolumeGroupAttributes volumeGroupAttrCopy; p.setDataPosition(0); EXPECT_EQ(NO_ERROR, volumeGroupAttrCopy.readFromParcel(&p)) << msg; EXPECT_EQ(volumeGroupAttrCopy.getGroupId(), volumeGroupAttr.getGroupId()) << msg; EXPECT_EQ(volumeGroupAttrCopy.getStreamType(), volumeGroupAttr.getStreamType()) << msg; EXPECT_TRUE(volumeGroupAttrCopy.getAttributes() == attributes) << msg; } // audioStream INSTANTIATE_TEST_SUITE_P(SerializationParameterizedTests, AudioAttributesParameterizedTest, ::testing::Combine(testing::ValuesIn(kStreamtypes)));