Add serialization tests for libaudioclient
Bug: 229419249
Test: atest audioclient_serialization_tests
Merged-In: I162ec7f820bb798b9320e1f0a917eaebad35647f
Change-Id: I162ec7f820bb798b9320e1f0a917eaebad35647f
diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp
index 0aea201..a61c939 100644
--- a/media/libaudioclient/tests/Android.bp
+++ b/media/libaudioclient/tests/Android.bp
@@ -169,3 +169,12 @@
"audio_test_utils.cpp",
],
}
+
+cc_test {
+ name: "audioclient_serialization_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: [
+ "audioclient_serialization_tests.cpp",
+ "audio_test_utils.cpp",
+ ],
+}
diff --git a/media/libaudioclient/tests/audioclient_serialization_tests.cpp b/media/libaudioclient/tests/audioclient_serialization_tests.cpp
new file mode 100644
index 0000000..93baefd6
--- /dev/null
+++ b/media/libaudioclient/tests/audioclient_serialization_tests.cpp
@@ -0,0 +1,310 @@
+/*
+ * 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,
+};
+
+// 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<AudioAttributes> audioattributesvector;
+ for (auto i = 0; i < 16; i++) {
+ audio_attributes_t attributes;
+ fillAudioAttributes(attributes);
+ AudioAttributes audioattributes{static_cast<volume_group_t>(rand()),
+ kStreamtypes[rand() % kStreamtypes.size()], attributes};
+ audioattributesvector.push_back(audioattributes);
+ }
+ product_strategy_t psId = static_cast<product_strategy_t>(rand());
+ AudioProductStrategy aps{name, audioattributesvector, 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.getAudioAttributes();
+ EXPECT_EQ(avec.size(), audioattributesvector.size()) << name;
+ for (int i = 0; i < audioattributesvector.size(); i++) {
+ EXPECT_EQ(avec[i].getGroupId(), audioattributesvector[i].getGroupId()) << name;
+ EXPECT_EQ(avec[i].getStreamType(), audioattributesvector[i].getStreamType()) << name;
+ EXPECT_TRUE(avec[i].getAttributes() == audioattributesvector[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)};
+ Vector<AudioMixMatchCriterion> criteria;
+ for (int i = 0; i < 16; i++) {
+ AudioMixMatchCriterion ammc{kUsages[rand() % kUsages.size()],
+ kInputSources[rand() % kInputSources.size()],
+ kMixMatchRules[rand() % kMixMatchRules.size()]};
+ criteria.add(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);
+ AudioAttributes audioattributes{groupId, stream, attributes};
+
+ Parcel p;
+ EXPECT_EQ(NO_ERROR, audioattributes.writeToParcel(&p)) << msg;
+
+ AudioAttributes audioattributesCopy;
+ p.setDataPosition(0);
+ EXPECT_EQ(NO_ERROR, audioattributesCopy.readFromParcel(&p)) << msg;
+ EXPECT_EQ(audioattributesCopy.getGroupId(), audioattributes.getGroupId()) << msg;
+ EXPECT_EQ(audioattributesCopy.getStreamType(), audioattributes.getStreamType()) << msg;
+ EXPECT_TRUE(audioattributesCopy.getAttributes() == attributes) << msg;
+}
+
+// audioStream
+INSTANTIATE_TEST_SUITE_P(SerializationParameterizedTests, AudioAttributesParameterizedTest,
+ ::testing::Combine(testing::ValuesIn(kStreamtypes)));