/*
* Copyright 2014 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 "C2SampleComponent_test"
#include <gtest/gtest.h>
#define __C2_GENERATE_GLOBAL_VARS__
#include <C2Component.h>
#include <C2Config.h>
#include <C2Debug.h>
#include <C2Enum.h>
#include <unordered_map>
class C2SampleComponentTest : public ::testing::Test {
};
void PrintTo(const C2FieldDescriptor &fd, ::std::ostream *os) {
using FD=C2FieldDescriptor;
switch (fd.type()) {
case FD::INT32: *os << "i32"; break;
case FD::INT64: *os << "i64"; break;
case FD::UINT32: *os << "u32"; break;
case FD::UINT64: *os << "u64"; break;
case FD::FLOAT: *os << "float"; break;
case FD::STRING: *os << "char"; break;
case FD::BLOB: *os << "u8"; break;
default:
if (fd.type() & FD::STRUCT_FLAG) {
*os << "struct-" << (fd.type() & ~FD::STRUCT_FLAG);
} else {
*os << "type-" << fd.type();
}
}
*os << " " << fd.name();
if (fd.extent() > 1) {
*os << "[" << fd.extent() << "]";
} else if (fd.extent() == 0) {
*os << "[]";
}
*os << " (" << fd._mFieldId << "*" << fd.extent() << ")";
}
C2ENUM(
MetadataType, int32_t,
kInvalid = -1,
kNone = 0,
kGralloc,
kNativeHandle,
kANativeWindow,
kCamera,
)
enum {
kParamIndexVideoConfig = 0x1234,
};
struct C2VideoConfigStruct {
int32_t width;
uint32_t height;
MetadataType metadataType;
int32_t supportedFormats[];
C2VideoConfigStruct() {}
DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(VideoConfig, supportedFormats)
C2FIELD(width, "width")
C2FIELD(height, "height")
C2FIELD(metadataType, "metadata-type")
C2FIELD(supportedFormats, "formats")
};
typedef C2PortParam<C2Tuning, C2VideoConfigStruct> C2VideoConfigPortTuning;
class MyComponentInstance : public C2ComponentInterface {
public:
virtual C2String getName() const override {
/// \todo this seems too specific
return "sample.interface";
};
virtual c2_node_id_t getId() const override {
/// \todo how are these shared?
return 0;
}
virtual c2_status_t config_vb(
const std::vector<C2Param*> ¶ms,
c2_blocking_t mayBlock,
std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
(void)params;
(void)failures;
(void)mayBlock;
return C2_OMITTED;
}
virtual c2_status_t createTunnel_sm(c2_node_id_t targetComponent) override {
(void)targetComponent;
return C2_OMITTED;
}
virtual c2_status_t query_vb(
const std::vector<C2Param*> &stackParams,
const std::vector<C2Param::Index> &heapParamIndices,
c2_blocking_t mayBlock,
std::vector<std::unique_ptr<C2Param>>* const heapParams) const override {
for (C2Param* const param : stackParams) {
(void)mayBlock;
if (!*param) { // param is already invalid - remember it
continue;
}
// note: this does not handle stream params (should use index...)
if (!mMyParams.count(param->index())) {
continue; // not my param
}
C2Param & myParam = mMyParams.find(param->index())->second;
if (myParam.size() != param->size()) { // incorrect size
param->invalidate();
continue;
}
param->updateFrom(myParam);
}
for (const C2Param::Index index : heapParamIndices) {
if (mMyParams.count(index)) {
C2Param & myParam = mMyParams.find(index)->second;
std::unique_ptr<C2Param> paramCopy(C2Param::Copy(myParam));
heapParams->push_back(std::move(paramCopy));
}
}
return C2_OK;
}
std::unordered_map<uint32_t, C2Param &> mMyParams;
C2ComponentDomainInfo mDomainInfo;
MyComponentInstance() {
mMyParams.insert({mDomainInfo.index(), mDomainInfo});
}
virtual c2_status_t releaseTunnel_sm(c2_node_id_t targetComponent) override {
(void)targetComponent;
return C2_OMITTED;
}
class MyParamReflector : public C2ParamReflector {
const MyComponentInstance *instance;
public:
MyParamReflector(const MyComponentInstance *i) : instance(i) { }
virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex paramIndex) const override {
switch (paramIndex.typeIndex()) {
case decltype(instance->mDomainInfo)::CORE_INDEX:
default:
return std::unique_ptr<C2StructDescriptor>(new C2StructDescriptor{
instance->mDomainInfo.type(),
decltype(instance->mDomainInfo)::FieldList(),
});
}
return nullptr;
}
};
virtual c2_status_t querySupportedValues_vb(
std::vector<C2FieldSupportedValuesQuery> &fields,
c2_blocking_t mayBlock) const override {
(void)mayBlock;
for (C2FieldSupportedValuesQuery &query : fields) {
if (query.field() == C2ParamField(&mDomainInfo, &C2ComponentDomainInfo::value)) {
query.values = C2FieldSupportedValues(
false /* flag */,
&mDomainInfo.value
//,
//{(int32_t)C2DomainVideo}
);
query.status = C2_OK;
} else {
query.status = C2_BAD_INDEX;
}
}
return C2_OK;
}
std::shared_ptr<C2ParamReflector> getParamReflector() const {
return std::shared_ptr<C2ParamReflector>(new MyParamReflector(this));
}
virtual c2_status_t querySupportedParams_nb(
std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const override {
params->push_back(std::make_shared<C2ParamDescriptor>(
true /* required */, "_domain", &mDomainInfo));
params->push_back(std::shared_ptr<C2ParamDescriptor>(
new C2ParamDescriptor(true /* required */, "_domain", &mDomainInfo)));
return C2_OK;
}
virtual ~MyComponentInstance() override = default;
};
template<typename E, bool S=std::is_enum<E>::value>
struct getter {
int32_t get(const C2FieldSupportedValues::Primitive &p, int32_t*) {
return p.i32;
}
int64_t get(const C2FieldSupportedValues::Primitive &p, int64_t*) {
return p.i64;
}
uint32_t get(const C2FieldSupportedValues::Primitive &p, uint32_t*) {
return p.u32;
}
uint64_t get(const C2FieldSupportedValues::Primitive &p, uint64_t*) {
return p.u64;
}
float get(const C2FieldSupportedValues::Primitive &p, float*) {
return p.fp;
}
};
template<typename E>
struct getter<E, true> {
typename std::underlying_type<E>::type get(const C2FieldSupportedValues::Primitive &p, E*) {
using u=typename std::underlying_type<E>::type;
return getter<u>().get(p, (u*)0);
}
};
template<typename T, bool E=std::is_enum<T>::value>
struct lax_underlying_type {
typedef typename std::underlying_type<T>::type type;
};
template<typename T>
struct lax_underlying_type<T, false> {
typedef T type;
};
template<typename E>
typename lax_underlying_type<E>::type get(
const C2FieldSupportedValues::Primitive &p, E*) {
return getter<E>().get(p, (E*)0);
}
template<typename T>
void dumpFSV(const C2FieldSupportedValues &sv, T*t) {
using namespace std;
cout << (std::is_enum<T>::value ? (std::is_signed<typename lax_underlying_type<T>::type>::value ? "i" : "u")
: std::is_integral<T>::value ? std::is_signed<T>::value ? "i" : "u" : "f")
<< (8 * sizeof(T));
if (sv.type == sv.RANGE) {
cout << ".range(" << get(sv.range.min, t);
if (get(sv.range.step, t) != std::is_integral<T>::value) {
cout << ":" << get(sv.range.step, t);
}
if (get(sv.range.num, t) != 1 || get(sv.range.denom, t) != 1) {
cout << ":" << get(sv.range.num, t) << "/" << get(sv.range.denom, t);
}
cout << get(sv.range.max, t) << ")";
}
if (sv.values.size()) {
cout << (sv.type == sv.FLAGS ? ".flags(" : ".list(");
const char *sep = "";
for (const C2FieldSupportedValues::Primitive &p : sv.values) {
cout << sep << get(p, t);
sep = ",";
}
cout << ")";
}
cout << endl;
}
void dumpType(C2Param::Type type) {
using namespace std;
cout << (type.isVendor() ? "Vendor" : "C2");
if (type.forInput()) {
cout << "Input";
} else if (type.forOutput()) {
cout << "Output";
} else if (type.forPort() && !type.forStream()) {
cout << "Port";
}
if (type.forStream()) {
cout << "Stream";
}
if (type.isFlexible()) {
cout << "Flex";
}
cout << type.typeIndex();
switch (type.kind()) {
case C2Param::INFO: cout << "Info"; break;
case C2Param::SETTING: cout << "Setting"; break;
case C2Param::TUNING: cout << "Tuning"; break;
case C2Param::STRUCT: cout << "Struct"; break;
default: cout << "Kind" << (int32_t)type.kind(); break;
}
}
void dumpType(C2Param::CoreIndex type) {
using namespace std;
cout << (type.isVendor() ? "Vendor" : "C2");
if (type.isFlexible()) {
cout << "Flex";
}
cout << type.typeIndex() << "Struct";
}
void dumpType(C2FieldDescriptor::type_t type) {
using namespace std;
switch (type) {
case C2FieldDescriptor::BLOB: cout << "blob "; break;
case C2FieldDescriptor::FLOAT: cout << "float "; break;
case C2FieldDescriptor::INT32: cout << "int32_t "; break;
case C2FieldDescriptor::INT64: cout << "int64_t "; break;
case C2FieldDescriptor::UINT32: cout << "uint32_t "; break;
case C2FieldDescriptor::UINT64: cout << "uint64_t "; break;
case C2FieldDescriptor::STRING: cout << "char "; break;
default:
cout << "struct ";
dumpType((C2Param::Type)type);
break;
}
}
void dumpStruct(const C2StructDescriptor &sd) {
using namespace std;
cout << "struct ";
dumpType(sd.coreIndex());
cout << " {" << endl;
//C2FieldDescriptor &f;
for (const C2FieldDescriptor &f : sd) {
PrintTo(f, &cout);
cout << endl;
if (f.namedValues().size()) {
cout << ".named(";
const char *sep = "";
for (const C2FieldDescriptor::NamedValueType &p : f.namedValues()) {
cout << sep << p.first << "=";
switch (f.type()) {
case C2Value::INT32: cout << get(p.second, (int32_t *)0); break;
case C2Value::INT64: cout << get(p.second, (int64_t *)0); break;
case C2Value::UINT32: cout << get(p.second, (uint32_t *)0); break;
case C2Value::UINT64: cout << get(p.second, (uint64_t *)0); break;
case C2Value::FLOAT: cout << get(p.second, (float *)0); break;
default: cout << "???"; break;
}
sep = ",";
}
cout << ")";
}
}
cout << "};" << endl;
}
void dumpDesc(const C2ParamDescriptor &pd) {
using namespace std;
if (pd.isRequired()) {
cout << "required ";
}
if (pd.isPersistent()) {
cout << "persistent ";
}
cout << "struct ";
dumpType(C2Param::Type(pd.index().type()));
cout << " " << pd.name() << ";" << endl;
}
TEST_F(C2SampleComponentTest, ReflectorTest) {
C2ComponentDomainInfo domainInfo;
std::shared_ptr<MyComponentInstance> myComp(new MyComponentInstance);
std::shared_ptr<C2ComponentInterface> comp = myComp;
std::unique_ptr<C2StructDescriptor> desc{
myComp->getParamReflector()->describe(C2ComponentDomainInfo::CORE_INDEX)};
dumpStruct(*desc);
std::vector<C2FieldSupportedValuesQuery> query = {
{ C2ParamField(&domainInfo, &C2ComponentDomainInfo::value),
C2FieldSupportedValuesQuery::CURRENT },
C2FieldSupportedValuesQuery(C2ParamField(&domainInfo, &C2ComponentDomainInfo::value),
C2FieldSupportedValuesQuery::CURRENT),
C2FieldSupportedValuesQuery::Current(C2ParamField(&domainInfo, &C2ComponentDomainInfo::value)),
};
EXPECT_EQ(C2_OK, comp->querySupportedValues_vb(query, C2_DONT_BLOCK));
for (const C2FieldSupportedValuesQuery &q : query) {
dumpFSV(q.values, &domainInfo.value);
}
}
TEST_F(C2SampleComponentTest, FieldSupportedValuesTest) {
typedef C2GlobalParam<C2Info, C2Uint32Value, 0> Uint32TestInfo;
Uint32TestInfo t;
std::vector<C2FieldSupportedValues> values;
values.push_back(C2FieldSupportedValues(0, 10, 1)); // min, max, step
values.push_back(C2FieldSupportedValues(1, 64, 2, 1)); // min, max, num, den
values.push_back(C2FieldSupportedValues(false, {1, 2, 3})); // flags, std::initializer_list
uint32_t val[] = {1, 3, 5, 7};
std::vector<uint32_t> v(std::begin(val), std::end(val));
values.push_back(C2FieldSupportedValues(false, v)); // flags, std::vector
for (const C2FieldSupportedValues &sv : values) {
dumpFSV(sv, &t.value);
}
}