/*
* 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