1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "XMLParserTest"
19
20 #include <utils/Log.h>
21
22 #include <fstream>
23
24 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
25
26 #include "XMLParserTestEnvironment.h"
27
28 #define XML_FILE_NAME "media_codecs_unit_test_caller.xml"
29
30 using namespace android;
31
32 static XMLParserTestEnvironment *gEnv = nullptr;
33
34 struct CodecProperties {
35 string codecName;
36 MediaCodecsXmlParser::CodecProperties codecProp;
37 };
38
39 struct RoleProperties {
40 string roleName;
41 string typeName;
42 string codecName;
43 bool isEncoder;
44 size_t order;
45 vector<pair<string, string>> attributeMap;
46 };
47
48 class XMLParseTest : public ::testing::Test {
49 public:
~XMLParseTest()50 ~XMLParseTest() {
51 if (mEleStream.is_open()) mEleStream.close();
52 mInputDataVector.clear();
53 mInputRoleVector.clear();
54 }
55
SetUp()56 virtual void SetUp() override { setUpDatabase(); }
57
58 void setUpDatabase();
59
60 void setCodecProperties(string codecName, bool isEncoder, int32_t order, set<string> quirkSet,
61 set<string> domainSet, set<string> variantSet, string typeName,
62 vector<pair<string, string>> domain, vector<string> aliases,
63 string rank);
64
65 void setRoleProperties(string roleName, bool isEncoder, int32_t order, string typeName,
66 string codecName, vector<pair<string, string>> domain);
67
68 void setServiceAttribute(map<string, string> serviceAttributeNameValuePair);
69
70 void printCodecMap(const MediaCodecsXmlParser::Codec mcodec);
71
72 void checkRoleMap(int32_t index, bool isEncoder, string typeName, string codecName,
73 vector<pair<string, string>> attrMap);
74
75 bool compareMap(const map<string, string> &lhs, const map<string, string> &rhs);
76
77 ifstream mEleStream;
78 MediaCodecsXmlParser mParser;
79 vector<CodecProperties> mInputDataVector;
80 vector<RoleProperties> mInputRoleVector;
81 map<string, string> mInputServiceAttributeMap;
82 };
83
setUpDatabase()84 void XMLParseTest::setUpDatabase() {
85 // The values set below are specific to test vector testdata/media_codecs_unit_test.xml
86 setCodecProperties("test1.decoder", false, 1, {"attribute::disabled", "quirk::quirk1"},
87 {"telephony"}, {}, "audio/mpeg", {}, {"alias1.decoder"}, "4");
88
89 setCodecProperties("test2.decoder", false, 2, {"quirk::quirk1"}, {}, {}, "audio/3gpp", {}, {},
90 "");
91
92 setCodecProperties("test3.decoder", false, 3, {}, {}, {}, "audio/amr-wb",
93 {
94 pair<string, string>("feature-feature1", "feature1Val"),
95 pair<string, string>("feature-feature2", "0"),
96 pair<string, string>("feature-feature3", "0"),
97 },
98 {}, "");
99
100 setCodecProperties("test4.decoder", false, 4, {}, {}, {}, "audio/flac",
101 {pair<string, string>("feature-feature1", "feature1Val")}, {}, "");
102
103 setCodecProperties("test5.decoder", false, 5, {"attribute::attributeQuirk1"}, {}, {},
104 "audio/g711-mlaw", {}, {}, "");
105
106 setCodecProperties("test6.decoder", false, 6, {}, {}, {"variant1", "variant2"},
107 "audio/mp4a-latm",
108 {pair<string, string>("variant1:::variant1Limit1-range",
109 "variant1Limit1Min-variant1Limit1Max"),
110 pair<string, string>("variant1:::variant1Limit2-range",
111 "variant1Limit2Low-variant1Limit2High"),
112 pair<string, string>("variant2:::variant2Limit1", "variant2Limit1Value")},
113 {}, "");
114
115 setCodecProperties(
116 "test7.decoder", false, 7, {}, {}, {}, "audio/vorbis",
117 {
118 pair<string, string>("-min-limit1", "limit1Min"),
119 /*pair<string, string>("limit1-in", "limit1In"),*/
120 pair<string, string>("limit2-range", "limit2Min-limit2Max"),
121 pair<string, string>("limit2-scale", "limit2Scale"),
122 pair<string, string>("limit3-default", "limit3Val3"),
123 pair<string, string>("limit3-ranges", "limit3Val1,limit3Val2,limit3Val3"),
124 },
125 {}, "");
126
127 setCodecProperties("test8.encoder", true, 8, {}, {}, {}, "audio/opus",
128 {pair<string, string>("max-limit1", "limit1Max")}, {}, "");
129
130 setCodecProperties("test9.encoder", true, 9, {}, {}, {}, "video/avc",
131 {
132 pair<string, string>("mapping-sure-before", "after"),
133 },
134 {}, "");
135
136 setCodecProperties("test10.encoder", true, 10, {}, {}, {}, "video/hevc",
137 {
138 pair<string, string>("mapping-fire-from", "to"),
139 },
140 {}, "");
141 setCodecProperties("test11.encoder", true, 11, {}, {}, {}, "video/av01",
142 {
143 pair<string, string>("tuning-hungry", "yes"),
144 pair<string, string>("tuning-pi", "3.1415"),
145 },
146 {}, "");
147
148 // minsdk
149 setCodecProperties("test12.encoder", true, 12, {"attribute::disabled"}, {}, {}, "video/t12",
150 {
151 pair<string, string>("tuning-enable-goal", "no"),
152 },
153 {}, "");
154 setCodecProperties("test13.encoder", true, 13, {"attribute::disabled"}, {}, {}, "video/t13",
155 {
156 pair<string, string>("tuning-enable-goal", "no"),
157 },
158 {}, "");
159 setCodecProperties("test14.encoder", true, 14, {"attribute::disabled"}, {}, {}, "video/t14",
160 {
161 pair<string, string>("tuning-enable-goal", "no"),
162 },
163 {}, "");
164 setCodecProperties("test15.encoder", true, 15, {}, {}, {}, "video/t15",
165 {
166 pair<string, string>("tuning-enable-goal", "yes"),
167 },
168 {}, "");
169 setCodecProperties("test16.encoder", true, 16, {}, {}, {}, "video/t16",
170 {
171 pair<string, string>("tuning-enable-goal", "yes"),
172 },
173 {}, "");
174
175 setRoleProperties("audio_decoder.mp3", false, 1, "audio/mpeg", "test1.decoder",
176 {pair<string, string>("attribute::disabled", "present"),
177 pair<string, string>("rank", "4")});
178
179 setRoleProperties("audio_decoder.amrnb", false, 2, "audio/3gpp", "test2.decoder", {});
180
181 setRoleProperties("audio_decoder.amrwb", false, 3, "audio/amr-wb", "test3.decoder",
182 {pair<string, string>("feature-feature1", "feature1Val"),
183 pair<string, string>("feature-feature2", "0"),
184 pair<string, string>("feature-feature3", "0")});
185
186 setRoleProperties("audio/flac", false, 4, "audio/flac", "test4.decoder",
187 {pair<string, string>("feature-feature1", "feature1Val")});
188
189 setRoleProperties("audio_decoder.g711mlaw", false, 5, "audio/g711-mlaw", "test5.decoder",
190 {pair<string, string>("attribute::attributeQuirk1", "present")});
191
192 setRoleProperties("audio_decoder.aac", false, 6, "audio/mp4a-latm", "test6.decoder",
193 {pair<string, string>("variant1:::variant1Limit1-range",
194 "variant1Limit1Min-variant1Limit1Max"),
195 pair<string, string>("variant1:::variant1Limit2-range",
196 "variant1Limit2Low-variant1Limit2High"),
197 pair<string, string>("variant2:::variant2Limit1", "variant2Limit1Value")});
198
199 setRoleProperties("audio_decoder.vorbis", false, 7, "audio/vorbis", "test7.decoder",
200 {pair<string, string>("-min-limit1", "limit1Min"),
201 /*pair<string, string>("limit1-in", "limit1In"),*/
202 pair<string, string>("limit2-range", "limit2Min-limit2Max"),
203 pair<string, string>("limit2-scale", "limit2Scale"),
204 pair<string, string>("limit3-default", "limit3Val3"),
205 pair<string, string>("limit3-ranges", "limit3Val1,limit3Val2,limit3Val3")});
206
207 setRoleProperties("audio_encoder.opus", true, 8, "audio/opus", "test8.encoder",
208 {pair<string, string>("max-limit1", "limit1Max")});
209
210 setRoleProperties("video_encoder.avc", true, 9, "video/avc", "test9.encoder",
211 {pair<string, string>("mapping-sure-before", "after")});
212
213 setRoleProperties("video_encoder.hevc", true, 10, "video/hevc", "test10.encoder",
214 { pair<string, string>("mapping-fire-from", "to")});
215
216 setRoleProperties("video_encoder.av01", true, 11, "video/av01", "test11.encoder",
217 {pair<string, string>("tuning-hungry", "yes"),
218 pair<string, string>("tuning-pi", "3.1415")
219 });
220
221 // minsdk
222 setRoleProperties("video_encoder.t12", true, 12, "video/t12", "test12.encoder",
223 {pair<string, string>("tuning-enable-goal", "no"),
224 pair<string, string>("attribute::disabled", "present") });
225 setRoleProperties("video_encoder.t13", true, 13, "video/t13", "test13.encoder",
226 {pair<string, string>("tuning-enable-goal", "no"),
227 pair<string, string>("attribute::disabled", "present") });
228 setRoleProperties("video_encoder.t14", true, 14, "video/t14", "test14.encoder",
229 {pair<string, string>("tuning-enable-goal", "no"),
230 pair<string, string>("attribute::disabled", "present") });
231 setRoleProperties("video_encoder.t15", true, 15, "video/t15", "test15.encoder",
232 {pair<string, string>("tuning-enable-goal", "yes")});
233 setRoleProperties("video_encoder.t16", true, 16, "video/t16", "test16.encoder",
234 {pair<string, string>("tuning-enable-goal", "yes")});
235
236
237 setServiceAttribute(
238 {pair<string, string>("domain-telephony", "0"), pair<string, string>("domain-tv", "0"),
239 pair<string, string>("setting2", "0"), pair<string, string>("variant-variant1", "0")});
240 }
241
compareMap(const map<string,string> & lhs,const map<string,string> & rhs)242 bool XMLParseTest::compareMap(const map<string, string> &lhs, const map<string, string> &rhs) {
243 return lhs.size() == rhs.size() && equal(lhs.begin(), lhs.end(), rhs.begin());
244 }
245
setCodecProperties(string codecName,bool isEncoder,int32_t order,set<string> quirkSet,set<string> domainSet,set<string> variantSet,string typeName,vector<pair<string,string>> domain,vector<string> aliases,string rank)246 void XMLParseTest::setCodecProperties(string codecName, bool isEncoder, int32_t order,
247 set<string> quirkSet, set<string> domainSet,
248 set<string> variantSet, string typeName,
249 vector<pair<string, string>> domain, vector<string> aliases,
250 string rank) {
251 map<string, string> AttributeMapDB;
252 for (const auto &AttrStr : domain) {
253 AttributeMapDB.insert(AttrStr);
254 }
255 map<string, MediaCodecsXmlParser::AttributeMap> TypeMapDataBase;
256 TypeMapDataBase.insert(
257 pair<string, MediaCodecsXmlParser::AttributeMap>(typeName, AttributeMapDB));
258 CodecProperties codecProperty;
259 codecProperty.codecName = codecName;
260 codecProperty.codecProp.isEncoder = isEncoder;
261 codecProperty.codecProp.order = order;
262 codecProperty.codecProp.quirkSet = quirkSet;
263 codecProperty.codecProp.domainSet = domainSet;
264 codecProperty.codecProp.variantSet = variantSet;
265 codecProperty.codecProp.typeMap = TypeMapDataBase;
266 codecProperty.codecProp.aliases = aliases;
267 codecProperty.codecProp.rank = rank;
268 mInputDataVector.push_back(codecProperty);
269 }
270
setRoleProperties(string roleName,bool isEncoder,int32_t order,string typeName,string codecName,vector<pair<string,string>> attributeNameValuePair)271 void XMLParseTest::setRoleProperties(string roleName, bool isEncoder, int32_t order,
272 string typeName, string codecName,
273 vector<pair<string, string>> attributeNameValuePair) {
274 struct RoleProperties roleProperty;
275 roleProperty.roleName = roleName;
276 roleProperty.typeName = typeName;
277 roleProperty.codecName = codecName;
278 roleProperty.isEncoder = isEncoder;
279 roleProperty.order = order;
280 roleProperty.attributeMap = attributeNameValuePair;
281 mInputRoleVector.push_back(roleProperty);
282 }
283
setServiceAttribute(map<string,string> serviceAttributeNameValuePair)284 void XMLParseTest::setServiceAttribute(map<string, string> serviceAttributeNameValuePair) {
285 for (const auto &serviceAttrStr : serviceAttributeNameValuePair) {
286 mInputServiceAttributeMap.insert(serviceAttrStr);
287 }
288 }
289
printCodecMap(const MediaCodecsXmlParser::Codec mcodec)290 void XMLParseTest::printCodecMap(const MediaCodecsXmlParser::Codec mcodec) {
291 const string &name = mcodec.first;
292 ALOGV("codec name = %s\n", name.c_str());
293 const MediaCodecsXmlParser::CodecProperties &properties = mcodec.second;
294 bool isEncoder = properties.isEncoder;
295 ALOGV("isEncoder = %d\n", isEncoder);
296 size_t order = properties.order;
297 ALOGV("order = %zu\n", order);
298 string rank = properties.rank;
299 ALOGV("rank = %s\n", rank.c_str());
300
301 for (auto &itrQuirkSet : properties.quirkSet) {
302 ALOGV("quirkSet= %s", itrQuirkSet.c_str());
303 }
304
305 for (auto &itrDomainSet : properties.domainSet) {
306 ALOGV("domainSet= %s", itrDomainSet.c_str());
307 }
308
309 for (auto &itrVariantSet : properties.variantSet) {
310 ALOGV("variantSet= %s", itrVariantSet.c_str());
311 }
312
313 map<string, MediaCodecsXmlParser::AttributeMap> TypeMap = properties.typeMap;
314 ALOGV("The TypeMap is :");
315
316 for (auto &itrTypeMap : TypeMap) {
317 ALOGV("itrTypeMap->first\t%s\t", itrTypeMap.first.c_str());
318
319 for (auto &itrAttributeMap : itrTypeMap.second) {
320 ALOGV("AttributeMap->first = %s", itrAttributeMap.first.c_str());
321 ALOGV("AttributeMap->second = %s", itrAttributeMap.second.c_str());
322 }
323 }
324 }
325
checkRoleMap(int32_t index,bool isEncoder,string typeName,string codecName,vector<pair<string,string>> AttributePairMap)326 void XMLParseTest::checkRoleMap(int32_t index, bool isEncoder, string typeName, string codecName,
327 vector<pair<string, string>> AttributePairMap) {
328 ASSERT_EQ(isEncoder, mInputRoleVector.at(index).isEncoder)
329 << "Invalid RoleMap data. IsEncoder mismatch";
330 ASSERT_EQ(typeName, mInputRoleVector.at(index).typeName)
331 << "Invalid RoleMap data. typeName mismatch";
332 ASSERT_EQ(codecName, mInputRoleVector.at(index).codecName)
333 << "Invalid RoleMap data. codecName mismatch";
334
335 vector<pair<string, string>>::iterator itr_attributeMapDB =
336 (mInputRoleVector.at(index).attributeMap).begin();
337 vector<pair<string, string>>::iterator itr_attributeMap = AttributePairMap.begin();
338 for (; itr_attributeMap != AttributePairMap.end() &&
339 itr_attributeMapDB != mInputRoleVector.at(index).attributeMap.end();
340 ++itr_attributeMap, ++itr_attributeMapDB) {
341 string attributeName = itr_attributeMap->first;
342 string attributeNameDB = itr_attributeMapDB->first;
343 string attributevalue = itr_attributeMap->second;
344 string attributeValueDB = itr_attributeMapDB->second;
345 ASSERT_EQ(attributeName, attributeNameDB)
346 << "Invalid RoleMap data. Attribute name mismatch\t" << attributeName << " != "
347 << "attributeNameDB";
348 ASSERT_EQ(attributevalue, attributeValueDB)
349 << "Invalid RoleMap data. Attribute value mismatch\t" << attributevalue << " != "
350 << "attributeValueDB";
351 }
352 }
353
TEST_F(XMLParseTest,CodecMapParseTest)354 TEST_F(XMLParseTest, CodecMapParseTest) {
355 string inputFileName = gEnv->getRes() + XML_FILE_NAME;
356 mEleStream.open(inputFileName, ifstream::binary);
357 ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open inputfile " << inputFileName;
358
359 mParser.parseXmlPath(inputFileName);
360 for (const MediaCodecsXmlParser::Codec &mcodec : mParser.getCodecMap()) {
361 printCodecMap(mcodec);
362 const MediaCodecsXmlParser::CodecProperties &properties = mcodec.second;
363 int32_t index = properties.order - 1;
364 ASSERT_GE(index, 0) << "Invalid order";
365 ASSERT_EQ(mInputDataVector.at(index).codecName, mcodec.first.c_str())
366 << "Invalid CodecMap data. codecName mismatch";
367 ASSERT_EQ(properties.isEncoder, mInputDataVector.at(index).codecProp.isEncoder)
368 << "Invalid CodecMap data. isEncoder mismatch";
369 ASSERT_EQ(properties.order, mInputDataVector.at(index).codecProp.order)
370 << "Invalid CodecMap data. order mismatch";
371
372 set<string> quirkSetDB = mInputDataVector.at(index).codecProp.quirkSet;
373 set<string> quirkSet = properties.quirkSet;
374 set<string> quirkDifference;
375 set_difference(quirkSetDB.begin(), quirkSetDB.end(), quirkSet.begin(), quirkSet.end(),
376 inserter(quirkDifference, quirkDifference.end()));
377 ASSERT_EQ(quirkDifference.size(), 0) << "CodecMap:quirk mismatch";
378
379 map<string, MediaCodecsXmlParser::AttributeMap> TypeMapDB =
380 mInputDataVector.at(index).codecProp.typeMap;
381 map<string, MediaCodecsXmlParser::AttributeMap> TypeMap = properties.typeMap;
382 map<string, MediaCodecsXmlParser::AttributeMap>::iterator itr_TypeMapDB = TypeMapDB.begin();
383 map<string, MediaCodecsXmlParser::AttributeMap>::iterator itr_TypeMap = TypeMap.begin();
384
385 ASSERT_EQ(TypeMapDB.size(), TypeMap.size())
386 << "Invalid CodecMap data. Typemap size mismatch";
387
388 for (; itr_TypeMap != TypeMap.end() && itr_TypeMapDB != TypeMapDB.end();
389 ++itr_TypeMap, ++itr_TypeMapDB) {
390 ASSERT_EQ(itr_TypeMap->first, itr_TypeMapDB->first)
391 << "Invalid CodecMap data. type mismatch";
392 bool flag = compareMap(itr_TypeMap->second, itr_TypeMapDB->second);
393 ASSERT_TRUE(flag) << "typeMap mismatch";
394 }
395 ASSERT_EQ(mInputDataVector.at(index).codecProp.rank, properties.rank)
396 << "Invalid CodecMap data. rank mismatch";
397 }
398 }
399
TEST_F(XMLParseTest,RoleMapParseTest)400 TEST_F(XMLParseTest, RoleMapParseTest) {
401 string inputFileName = gEnv->getRes() + XML_FILE_NAME;
402 mEleStream.open(inputFileName, ifstream::binary);
403 ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open inputfile " << inputFileName;
404
405 mParser.parseXmlPath(inputFileName);
406
407 for (auto &mRole : mParser.getRoleMap()) {
408 typedef pair<string, string> Attribute;
409 const string &roleName = mRole.first;
410 ALOGV("Role map:name = %s\n", roleName.c_str());
411 const MediaCodecsXmlParser::RoleProperties &properties = mRole.second;
412 string type = properties.type;
413 ALOGV("Role map: type = %s\n", type.c_str());
414
415 bool isEncoder = properties.isEncoder;
416 ALOGV("Role map: isEncoder = %d\n", isEncoder);
417
418 multimap<size_t, MediaCodecsXmlParser::NodeInfo> nodeList = properties.nodeList;
419 multimap<size_t, MediaCodecsXmlParser::NodeInfo>::iterator itr_Node;
420 ALOGV("\nThe multimap nodeList is : \n");
421 for (itr_Node = nodeList.begin(); itr_Node != nodeList.end(); ++itr_Node) {
422 ALOGV("itr_Node->first=ORDER=\t%zu\t", itr_Node->first);
423 int32_t index = itr_Node->first - 1;
424 MediaCodecsXmlParser::NodeInfo nodePtr = itr_Node->second;
425 ALOGV("Role map:itr_Node->second.name = %s\n", nodePtr.name.c_str());
426 vector<Attribute> attrList = nodePtr.attributeList;
427 for (auto attrNameValueList = attrList.begin(); attrNameValueList != attrList.end();
428 ++attrNameValueList) {
429 ALOGV("Role map:nodePtr.attributeList->first = %s\n",
430 attrNameValueList->first.c_str());
431 ALOGV("Role map:nodePtr.attributeList->second = %s\n",
432 attrNameValueList->second.c_str());
433 }
434 checkRoleMap(index, isEncoder, properties.type, nodePtr.name.c_str(), attrList);
435 }
436 }
437 }
438
TEST_F(XMLParseTest,ServiceAttributeMapParseTest)439 TEST_F(XMLParseTest, ServiceAttributeMapParseTest) {
440 string inputFileName = gEnv->getRes() + XML_FILE_NAME;
441 mEleStream.open(inputFileName, ifstream::binary);
442 ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open inputfile " << inputFileName;
443
444 mParser.parseXmlPath(inputFileName);
445 const auto serviceAttributeMap = mParser.getServiceAttributeMap();
446 for (const auto &attributePair : serviceAttributeMap) {
447 ALOGV("serviceAttribute.key = %s \t serviceAttribute.value = %s",
448 attributePair.first.c_str(), attributePair.second.c_str());
449 }
450 bool flag = compareMap(mInputServiceAttributeMap, serviceAttributeMap);
451 ASSERT_TRUE(flag) << "ServiceMapParseTest: typeMap mismatch";
452 }
453
main(int argc,char ** argv)454 int main(int argc, char **argv) {
455 gEnv = new XMLParserTestEnvironment();
456 ::testing::AddGlobalTestEnvironment(gEnv);
457 ::testing::InitGoogleTest(&argc, argv);
458 int status = gEnv->initFromOptions(argc, argv);
459 if (status == 0) {
460 status = RUN_ALL_TESTS();
461 ALOGD("XML Parser Test Result = %d\n", status);
462 }
463 return status;
464 }
465