/* * Copyright 2018 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 __C2_GENERATE_GLOBAL_VARS__ #include <set> #include <gtest/gtest.h> #include <C2ParamDef.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/hexdump.h> #include <ReflectedParamUpdater.h> namespace android { namespace { enum { kParamIndexTestStart = 0x1000, kParamIndexInt, kParamIndexString, kParamIndexComposite, kParamIndexFlexString, kParamIndexLong = C2Param::TYPE_INDEX_VENDOR_START, }; typedef C2GlobalParam<C2Info, C2Int32Value, kParamIndexInt> C2IntInfo; typedef C2GlobalParam<C2Info, C2Int64Value, kParamIndexLong> C2LongInfo; struct C2FixedSizeStringStruct { char value[12]; DEFINE_AND_DESCRIBE_BASE_C2STRUCT(FixedSizeString) C2FIELD(value, "value") }; typedef C2GlobalParam<C2Info, C2FixedSizeStringStruct, kParamIndexString> C2StringInfo; struct C2CompositeStruct { int32_t i32; uint64_t u64; char str[12]; uint8_t blob[8]; uint8_t flexBlob[]; C2CompositeStruct() = default; DEFINE_AND_DESCRIBE_BASE_FLEX_C2STRUCT(Composite, flexBlob) C2FIELD(i32, "i32") C2FIELD(u64, "u64") C2FIELD(str, "str") C2FIELD(blob, "blob") C2FIELD(flexBlob, "flex-blob") }; static_assert(C2CompositeStruct::FLEX_SIZE == 1, ""); static_assert(_C2FlexHelper<C2CompositeStruct>::FLEX_SIZE == 1, ""); typedef C2GlobalParam<C2Info, C2CompositeStruct, kParamIndexComposite> C2CompositeInfo; typedef C2GlobalParam<C2Info, C2StringValue, kParamIndexFlexString> C2FlexStringInfo; #define SUPPORTED_TYPES \ C2IntInfo, \ C2LongInfo, \ C2StringInfo, \ C2CompositeInfo, \ C2FlexStringInfo template<typename... TYPES> struct describe_impl; template<typename T, typename... TYPES> struct describe_impl<T, TYPES...> { static std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex index) { if (index == T::CORE_INDEX) { return std::make_unique<C2StructDescriptor>(T::CORE_INDEX, T::FieldList()); } else { return describe_impl<TYPES...>::describe(index); } } }; template<> struct describe_impl<> { static std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex) { return nullptr; } }; template<typename T> const char *GetName() { return nullptr; } template<> const char *GetName<C2IntInfo>() { return "int"; } template<> const char *GetName<C2LongInfo>() { return "long"; } template<> const char *GetName<C2StringInfo>() { return "string"; } template<> const char *GetName<C2CompositeInfo>() { return "composite"; } template<> const char *GetName<C2FlexStringInfo>() { return "flex-string"; } template<typename... TYPES> struct fill_descriptors_impl; template<typename T, typename... TYPES> struct fill_descriptors_impl<T, TYPES...> { static void fill(std::vector<std::shared_ptr<C2ParamDescriptor>> *vec) { fill_descriptors_impl<TYPES...>::fill(vec); vec->push_back(std::make_shared<C2ParamDescriptor>( T::PARAM_TYPE, C2ParamDescriptor::IS_PERSISTENT, GetName<T>())); } }; template<> struct fill_descriptors_impl<> { static void fill(std::vector<std::shared_ptr<C2ParamDescriptor>> *) {} }; template<typename T> T *CastParam(const std::unique_ptr<C2Param> ¶m) { return (T *)param.get(); } class ParamReflector : public C2ParamReflector { public: ParamReflector() = default; ~ParamReflector() override = default; std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex paramIndex) const override { return describe_impl<SUPPORTED_TYPES>::describe(paramIndex); } }; } // namespace class ReflectedParamUpdaterTest : public ::testing::Test { public: ReflectedParamUpdaterTest() : mReflector(new ParamReflector) { fill_descriptors_impl<SUPPORTED_TYPES>::fill(&mDescriptors); } std::shared_ptr<C2ParamReflector> mReflector; std::vector<std::shared_ptr<C2ParamDescriptor>> mDescriptors; }; TEST_F(ReflectedParamUpdaterTest, SingleValueTest) { ReflectedParamUpdater updater; ReflectedParamUpdater::Dict msg; msg.emplace("int.value", int32_t(12)); msg.emplace("vendor.long.value", int64_t(34)); updater.addParamDesc(mReflector, mDescriptors); std::vector<C2Param::Index> indices; updater.getParamIndicesFromMessage(msg, &indices); EXPECT_EQ(1, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2IntInfo::PARAM_TYPE; })); EXPECT_EQ(1, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2LongInfo::PARAM_TYPE; })); EXPECT_EQ(0, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2StringInfo::PARAM_TYPE; })); EXPECT_EQ(0, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2CompositeInfo::PARAM_TYPE; })); EXPECT_EQ(0, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2FlexStringInfo::PARAM_TYPE; })); std::vector<std::unique_ptr<C2Param>> params; params.emplace_back(new C2IntInfo); params.emplace_back(new C2LongInfo); EXPECT_EQ(0, CastParam<C2IntInfo>(params[0])->value); EXPECT_EQ(0, CastParam<C2LongInfo>(params[1])->value); updater.updateParamsFromMessage(msg, ¶ms); EXPECT_EQ(12, CastParam<C2IntInfo>(params[0])->value); EXPECT_EQ(34, CastParam<C2LongInfo>(params[1])->value); C2Value c2Value; int32_t int32Value = 0; int64_t int64Value = 0; msg = updater.getParams(params); ASSERT_EQ(1u, msg.count("int.value")); EXPECT_EQ(true, msg["int.value"].find(&c2Value)); EXPECT_EQ(true, c2Value.get(&int32Value)); EXPECT_EQ(12, int32Value); ASSERT_EQ(1u, msg.count("vendor.long.value")); EXPECT_EQ(true, msg["vendor.long.value"].find(&c2Value)); EXPECT_EQ(true, c2Value.get(&int64Value)); EXPECT_EQ(34, int64Value); } TEST_F(ReflectedParamUpdaterTest, StringTest) { ReflectedParamUpdater updater; ReflectedParamUpdater::Dict msg; msg.emplace("string.value", AString("56")); msg.emplace("flex-string.value", AString("Some string")); updater.addParamDesc(mReflector, mDescriptors); std::vector<C2Param::Index> indices; updater.getParamIndicesFromMessage(msg, &indices); EXPECT_EQ(0, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2IntInfo::PARAM_TYPE; })); EXPECT_EQ(0, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2LongInfo::PARAM_TYPE; })); EXPECT_EQ(1, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2StringInfo::PARAM_TYPE; })); EXPECT_EQ(0, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2CompositeInfo::PARAM_TYPE; })); EXPECT_EQ(1, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2FlexStringInfo::PARAM_TYPE; })); std::vector<std::unique_ptr<C2Param>> params; params.emplace_back(new C2StringInfo); EXPECT_EQ(0, CastParam<C2StringInfo>(params[0])->value[0]); params.emplace_back(C2FlexStringInfo::AllocUnique(0)); EXPECT_EQ(0u, CastParam<C2FlexStringInfo>(params[1])->flexCount()); char *flexStringData = &CastParam<C2FlexStringInfo>(params[1])->m.value[0]; updater.updateParamsFromMessage(msg, ¶ms); EXPECT_STREQ("56", CastParam<C2StringInfo>(params[0])->value); EXPECT_EQ(12u, CastParam<C2FlexStringInfo>(params[0])->flexCount()); EXPECT_STREQ("Some string", CastParam<C2FlexStringInfo>(params[1])->m.value); EXPECT_NE(flexStringData, &CastParam<C2FlexStringInfo>(params[1])->m.value[0]); flexStringData = &CastParam<C2FlexStringInfo>(params[1])->m.value[0]; // verify truncation and in-place update msg["string.value"] = ReflectedParamUpdater::Value(AString("1234567890ABCDE")); msg["flex-string.value"] = ReflectedParamUpdater::Value(AString("abc")); updater.updateParamsFromMessage(msg, ¶ms); EXPECT_STREQ("1234567890A", CastParam<C2StringInfo>(params[0])->value); EXPECT_EQ(4u, CastParam<C2FlexStringInfo>(params[1])->flexCount()); EXPECT_STREQ("abc", CastParam<C2FlexStringInfo>(params[1])->m.value); EXPECT_EQ(flexStringData, &CastParam<C2FlexStringInfo>(params[1])->m.value[0]); AString strValue; msg = updater.getParams(params); ASSERT_EQ(1u, msg.count("string.value")); EXPECT_EQ(true, msg["string.value"].find(&strValue)); EXPECT_STREQ("1234567890A", strValue.c_str()); ASSERT_EQ(1u, msg.count("flex-string.value")); EXPECT_EQ(true, msg["flex-string.value"].find(&strValue)); EXPECT_STREQ("abc", strValue.c_str()); } TEST_F(ReflectedParamUpdaterTest, CompositeTest) { ReflectedParamUpdater updater; ReflectedParamUpdater::Dict msg; msg.emplace("composite.i32", int32_t(78)); msg.emplace("composite.u64", int64_t(910)); msg.emplace("composite.str", AString("1112")); msg.emplace("composite.blob", ABuffer::CreateAsCopy("buffer08", 8)); msg.emplace("composite.flex-blob", ABuffer::CreateAsCopy("flex-buffer-14", 14)); updater.addParamDesc(mReflector, mDescriptors); std::vector<C2Param::Index> indices; updater.getParamIndicesFromMessage(msg, &indices); EXPECT_EQ(0, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2IntInfo::PARAM_TYPE; })); EXPECT_EQ(0, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2LongInfo::PARAM_TYPE; })); EXPECT_EQ(0, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2StringInfo::PARAM_TYPE; })); EXPECT_EQ(1, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2CompositeInfo::PARAM_TYPE; })); std::vector<std::unique_ptr<C2Param>> params; params.emplace_back(C2CompositeInfo::AllocUnique(0)); EXPECT_EQ(0, CastParam<C2CompositeInfo>(params[0])->m.i32); EXPECT_EQ(0u, CastParam<C2CompositeInfo>(params[0])->m.u64); EXPECT_EQ(0, CastParam<C2CompositeInfo>(params[0])->m.str[0]); EXPECT_EQ(0, memcmp("\0\0\0\0\0\0\0\0", CastParam<C2CompositeInfo>(params[0])->m.blob, 8)); EXPECT_EQ(0u, CastParam<C2CompositeInfo>(params[0])->flexCount()); uint8_t *flexBlobData = &CastParam<C2CompositeInfo>(params[0])->m.flexBlob[0]; updater.updateParamsFromMessage(msg, ¶ms); EXPECT_EQ(78, CastParam<C2CompositeInfo>(params[0])->m.i32); EXPECT_EQ(910u, CastParam<C2CompositeInfo>(params[0])->m.u64); EXPECT_STREQ("1112", CastParam<C2CompositeInfo>(params[0])->m.str); EXPECT_EQ(0, memcmp("buffer08", CastParam<C2CompositeInfo>(params[0])->m.blob, 8)); AString hex; hexdump(CastParam<C2CompositeInfo>(params[0])->m.blob, 8, 0, &hex); printf("%s\n", hex.c_str()); ASSERT_EQ(14u, CastParam<C2CompositeInfo>(params[0])->flexCount()); EXPECT_EQ(0, memcmp("flex-buffer-14", CastParam<C2CompositeInfo>(params[0])->m.flexBlob, 14)); EXPECT_NE(flexBlobData, &CastParam<C2CompositeInfo>(params[0])->m.flexBlob[0]); flexBlobData = &CastParam<C2CompositeInfo>(params[0])->m.flexBlob[0]; // test setting and zero extending shorter blob than allowed msg.clear(); msg.emplace("composite.blob", ABuffer::CreateAsCopy("buf05", 5)); updater.updateParamsFromMessage(msg, ¶ms); EXPECT_EQ(0, memcmp("buf05\0\0\0", CastParam<C2CompositeInfo>(params[0])->m.blob, 8)); ASSERT_EQ(14u, CastParam<C2CompositeInfo>(params[0])->flexCount()); EXPECT_EQ(0, memcmp("flex-buffer-14", CastParam<C2CompositeInfo>(params[0])->m.flexBlob, 14)); EXPECT_EQ(flexBlobData, &CastParam<C2CompositeInfo>(params[0])->m.flexBlob[0]); // test setting and trimming larger blob than allowed msg.clear(); msg.emplace("composite.blob", ABuffer::CreateAsCopy("ReallyLongBuffer", 16)); updater.updateParamsFromMessage(msg, ¶ms); EXPECT_EQ(0, memcmp("ReallyLo", CastParam<C2CompositeInfo>(params[0])->m.blob, 8)); ASSERT_EQ(14u, CastParam<C2CompositeInfo>(params[0])->flexCount()); EXPECT_EQ(0, memcmp("flex-buffer-14", CastParam<C2CompositeInfo>(params[0])->m.flexBlob, 14)); EXPECT_EQ(flexBlobData, &CastParam<C2CompositeInfo>(params[0])->m.flexBlob[0]); // test trimming flex blob in-place msg.clear(); msg.emplace("composite.flex-blob", ABuffer::CreateAsCopy("buf05", 5)); updater.updateParamsFromMessage(msg, ¶ms); ASSERT_EQ(5u, CastParam<C2CompositeInfo>(params[0])->flexCount()); EXPECT_EQ(0, memcmp("buf05", CastParam<C2CompositeInfo>(params[0])->m.flexBlob, 5)); EXPECT_EQ(flexBlobData, &CastParam<C2CompositeInfo>(params[0])->m.flexBlob[0]); } TEST_F(ReflectedParamUpdaterTest, CompositePartialTest) { ReflectedParamUpdater updater; ReflectedParamUpdater::Dict msg; msg.emplace("composite.i32", C2Value(1314)); msg.emplace("composite.str", AString("1516")); updater.addParamDesc(mReflector, mDescriptors); std::vector<C2Param::Index> indices; updater.getParamIndicesFromMessage(msg, &indices); EXPECT_EQ(0, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2IntInfo::PARAM_TYPE; })); EXPECT_EQ(0, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2LongInfo::PARAM_TYPE; })); EXPECT_EQ(0, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2StringInfo::PARAM_TYPE; })); EXPECT_EQ(1, std::count_if(indices.begin(), indices.end(), [](const auto &value) { return (uint32_t)value == C2CompositeInfo::PARAM_TYPE; })); std::vector<std::unique_ptr<C2Param>> params; params.emplace_back(C2CompositeInfo::AllocUnique(12u)); EXPECT_EQ(0, CastParam<C2CompositeInfo>(params[0])->m.i32); EXPECT_EQ(0u, CastParam<C2CompositeInfo>(params[0])->m.u64); EXPECT_EQ(0, CastParam<C2CompositeInfo>(params[0])->m.str[0]); updater.updateParamsFromMessage(msg, ¶ms); EXPECT_EQ(1314, CastParam<C2CompositeInfo>(params[0])->m.i32); EXPECT_EQ(0u, CastParam<C2CompositeInfo>(params[0])->m.u64); EXPECT_STREQ("1516", CastParam<C2CompositeInfo>(params[0])->m.str); } } // namespace android