313 lines
13 KiB
C++
313 lines
13 KiB
C++
|
|
/*
|
||
|
|
* 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 <cstdint>
|
||
|
|
#include <cstdlib>
|
||
|
|
#include <ctime>
|
||
|
|
|
||
|
|
#include <gtest/gtest.h>
|
||
|
|
|
||
|
|
#include <android_audio_policy_configuration_V7_0-enums.h>
|
||
|
|
#include <xsdc/XsdcSupport.h>
|
||
|
|
|
||
|
|
#include "audio_test_utils.h"
|
||
|
|
|
||
|
|
using namespace android;
|
||
|
|
namespace xsd {
|
||
|
|
using namespace ::android::audio::policy::configuration::V7_0;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename T, typename X, typename FUNC>
|
||
|
|
std::vector<T> getFlags(const xsdc_enum_range<X>& range, const FUNC& func,
|
||
|
|
const std::string& findString = {}) {
|
||
|
|
std::vector<T> 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<audio_usage_t> kUsages =
|
||
|
|
getFlags<audio_usage_t, xsd::AudioUsage, decltype(audio_usage_from_string)>(
|
||
|
|
xsdc_enum_range<xsd::AudioUsage>{}, audio_usage_from_string);
|
||
|
|
|
||
|
|
static const std::vector<audio_content_type_t> kContentType =
|
||
|
|
getFlags<audio_content_type_t, xsd::AudioContentType,
|
||
|
|
decltype(audio_content_type_from_string)>(xsdc_enum_range<xsd::AudioContentType>{},
|
||
|
|
audio_content_type_from_string);
|
||
|
|
|
||
|
|
static const std::vector<audio_source_t> kInputSources =
|
||
|
|
getFlags<audio_source_t, xsd::AudioSource, decltype(audio_source_from_string)>(
|
||
|
|
xsdc_enum_range<xsd::AudioSource>{}, audio_source_from_string);
|
||
|
|
|
||
|
|
static const std::vector<audio_stream_type_t> kStreamtypes =
|
||
|
|
getFlags<audio_stream_type_t, xsd::AudioStreamType,
|
||
|
|
decltype(audio_stream_type_from_string)>(xsdc_enum_range<xsd::AudioStreamType>{},
|
||
|
|
audio_stream_type_from_string);
|
||
|
|
|
||
|
|
static const std::vector<uint32_t> 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<unsigned int>(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<audio_flags_mask_t>(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<unsigned int>(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<VolumeGroupAttributes> volumeGroupAttrVector;
|
||
|
|
for (auto i = 0; i < 16; i++) {
|
||
|
|
audio_attributes_t attributes;
|
||
|
|
fillAudioAttributes(attributes);
|
||
|
|
VolumeGroupAttributes volumeGroupAttr{static_cast<volume_group_t>(rand()),
|
||
|
|
kStreamtypes[rand() % kStreamtypes.size()],
|
||
|
|
attributes};
|
||
|
|
volumeGroupAttrVector.push_back(volumeGroupAttr);
|
||
|
|
}
|
||
|
|
product_strategy_t psId = static_cast<product_strategy_t>(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<volume_group_t>(rand());
|
||
|
|
std::vector<audio_attributes_t> attributesvector;
|
||
|
|
for (auto i = 0; i < 16; i++) {
|
||
|
|
audio_attributes_t attributes;
|
||
|
|
fillAudioAttributes(attributes);
|
||
|
|
attributesvector.push_back(attributes);
|
||
|
|
}
|
||
|
|
std::vector<audio_stream_type_t> 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<AudioMixMatchCriterion> 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<uint32_t>(rand()),
|
||
|
|
config,
|
||
|
|
static_cast<uint32_t>(rand()),
|
||
|
|
String8(msg.c_str()),
|
||
|
|
static_cast<uint32_t>(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<audio_usage_t, audio_source_t, uint32_t>;
|
||
|
|
|
||
|
|
class MMCParameterizedTest : public FillAudioAttributes,
|
||
|
|
public ::testing::TestWithParam<MMCTestParams> {
|
||
|
|
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<unsigned int>(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<audio_stream_type_t>;
|
||
|
|
|
||
|
|
class AudioAttributesParameterizedTest
|
||
|
|
: public FillAudioAttributes,
|
||
|
|
public ::testing::TestWithParam<AudioAttributesTestParams> {
|
||
|
|
public:
|
||
|
|
AudioAttributesParameterizedTest() : mAudioStream(std::get<0>(GetParam())){};
|
||
|
|
|
||
|
|
const audio_stream_type_t mAudioStream;
|
||
|
|
|
||
|
|
void SetUp() override {
|
||
|
|
mSeed = static_cast<unsigned int>(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<volume_group_t>(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)));
|