// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//#define LOG_NDEBUG 0
#define LOG_TAG "C2VDACompIntf_test"
#include <C2VDAAllocatorStore.h>
#include <C2VDAComponent.h>
#include <C2PlatformSupport.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
#include <inttypes.h>
#include <stdio.h>
#include <limits>
#define UNUSED(expr) \
do { \
(void)(expr); \
} while (0)
namespace android {
const C2String testCompName = "c2.vda.avc.decoder";
const c2_node_id_t testCompNodeId = 12345;
const char* MEDIA_MIMETYPE_VIDEO_RAW = "video/raw";
const char* MEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
const C2Allocator::id_t kInputAllocators[] = {C2PlatformAllocatorStore::ION};
const C2Allocator::id_t kOutputAllocators[] = {C2VDAAllocatorStore::V4L2_BUFFERQUEUE};
const C2BlockPool::local_id_t kDefaultOutputBlockPool = C2BlockPool::BASIC_GRAPHIC;
class C2VDACompIntfTest : public ::testing::Test {
protected:
C2VDACompIntfTest() {
mReflector = std::make_shared<C2ReflectorHelper>();
mIntf = std::shared_ptr<C2ComponentInterface>(new SimpleInterface<C2VDAComponent::IntfImpl>(
testCompName.c_str(), testCompNodeId,
std::make_shared<C2VDAComponent::IntfImpl>(testCompName, mReflector)));
}
~C2VDACompIntfTest() override {}
template <typename T>
void testReadOnlyParam(const T* expected, T* invalid);
template <typename T>
void checkReadOnlyFailureOnConfig(T* param);
template <typename T>
void testReadOnlyParamOnStack(const T* expected, T* invalid);
template <typename T>
void testReadOnlyParamOnHeap(const T* expected, T* invalid);
template <typename T>
void testWritableParam(T* newParam);
template <typename T>
void testInvalidWritableParam(T* invalidParam);
template <typename T>
void testWritableVideoSizeParam(int32_t widthMin, int32_t widthMax, int32_t widthStep,
int32_t heightMin, int32_t heightMax, int32_t heightStep);
std::shared_ptr<C2ComponentInterface> mIntf;
std::shared_ptr<C2ReflectorHelper> mReflector;
};
template <typename T>
void C2VDACompIntfTest::testReadOnlyParam(const T* expected, T* invalid) {
testReadOnlyParamOnStack(expected, invalid);
testReadOnlyParamOnHeap(expected, invalid);
}
template <typename T>
void C2VDACompIntfTest::checkReadOnlyFailureOnConfig(T* param) {
std::vector<C2Param*> params{param};
std::vector<std::unique_ptr<C2SettingResult>> failures;
// TODO: do not assert on checking return value since it is not consistent for
// C2InterfaceHelper now. (b/79720928)
// 1) if config same value, it returns C2_OK
// 2) if config different value, it returns C2_CORRUPTED. But when you config again, it
// returns C2_OK
//ASSERT_EQ(C2_BAD_VALUE, mIntf->config_vb(params, C2_DONT_BLOCK, &failures));
mIntf->config_vb(params, C2_DONT_BLOCK, &failures);
// TODO: failure is not yet supported for C2InterfaceHelper
//ASSERT_EQ(1u, failures.size());
//EXPECT_EQ(C2SettingResult::READ_ONLY, failures[0]->failure);
}
template <typename T>
void C2VDACompIntfTest::testReadOnlyParamOnStack(const T* expected, T* invalid) {
T param;
std::vector<C2Param*> stackParams{¶m};
ASSERT_EQ(C2_OK, mIntf->query_vb(stackParams, {}, C2_DONT_BLOCK, nullptr));
EXPECT_EQ(*expected, param);
checkReadOnlyFailureOnConfig(¶m);
checkReadOnlyFailureOnConfig(invalid);
// The param must not change after failed config.
ASSERT_EQ(C2_OK, mIntf->query_vb(stackParams, {}, C2_DONT_BLOCK, nullptr));
EXPECT_EQ(*expected, param);
}
template <typename T>
void C2VDACompIntfTest::testReadOnlyParamOnHeap(const T* expected, T* invalid) {
std::vector<std::unique_ptr<C2Param>> heapParams;
uint32_t index = expected->index();
ASSERT_EQ(C2_OK, mIntf->query_vb({}, {index}, C2_DONT_BLOCK, &heapParams));
ASSERT_EQ(1u, heapParams.size());
EXPECT_EQ(*expected, *heapParams[0]);
checkReadOnlyFailureOnConfig(heapParams[0].get());
checkReadOnlyFailureOnConfig(invalid);
// The param must not change after failed config.
heapParams.clear();
ASSERT_EQ(C2_OK, mIntf->query_vb({}, {index}, C2_DONT_BLOCK, &heapParams));
ASSERT_EQ(1u, heapParams.size());
EXPECT_EQ(*expected, *heapParams[0]);
}
template <typename T>
void C2VDACompIntfTest::testWritableParam(T* newParam) {
std::vector<C2Param*> params{newParam};
std::vector<std::unique_ptr<C2SettingResult>> failures;
ASSERT_EQ(C2_OK, mIntf->config_vb(params, C2_DONT_BLOCK, &failures));
EXPECT_EQ(0u, failures.size());
// The param must change to newParam
// Check like param on stack
T param;
std::vector<C2Param*> stackParams{¶m};
ASSERT_EQ(C2_OK, mIntf->query_vb(stackParams, {}, C2_DONT_BLOCK, nullptr));
EXPECT_EQ(*newParam, param);
// Check also like param on heap
std::vector<std::unique_ptr<C2Param>> heapParams;
ASSERT_EQ(C2_OK, mIntf->query_vb({}, {newParam->index()}, C2_DONT_BLOCK, &heapParams));
ASSERT_EQ(1u, heapParams.size());
EXPECT_EQ(*newParam, *heapParams[0]);
}
template <typename T>
void C2VDACompIntfTest::testInvalidWritableParam(T* invalidParam) {
// Get the current parameter info
T preParam;
std::vector<C2Param*> stackParams{&preParam};
ASSERT_EQ(C2_OK, mIntf->query_vb(stackParams, {}, C2_DONT_BLOCK, nullptr));
// Config invalid value. The failure is expected
std::vector<C2Param*> params{invalidParam};
std::vector<std::unique_ptr<C2SettingResult>> failures;
ASSERT_EQ(C2_BAD_VALUE, mIntf->config_vb(params, C2_DONT_BLOCK, &failures));
EXPECT_EQ(1u, failures.size());
//The param must not change after config failed
T param;
std::vector<C2Param*> stackParams2{¶m};
ASSERT_EQ(C2_OK, mIntf->query_vb(stackParams2, {}, C2_DONT_BLOCK, nullptr));
EXPECT_EQ(preParam, param);
// Check also like param on heap
std::vector<std::unique_ptr<C2Param>> heapParams;
ASSERT_EQ(C2_OK, mIntf->query_vb({}, {invalidParam->index()}, C2_DONT_BLOCK, &heapParams));
ASSERT_EQ(1u, heapParams.size());
EXPECT_EQ(preParam, *heapParams[0]);
}
bool isUnderflowSubstract(int32_t a, int32_t b) {
return a < 0 && b > a - std::numeric_limits<int32_t>::min();
}
bool isOverflowAdd(int32_t a, int32_t b) {
return a > 0 && b > std::numeric_limits<int32_t>::max() - a;
}
template <typename T>
void C2VDACompIntfTest::testWritableVideoSizeParam(int32_t widthMin, int32_t widthMax,
int32_t widthStep, int32_t heightMin,
int32_t heightMax, int32_t heightStep) {
// Test supported values of video size
T valid;
for (int32_t h = heightMin; h <= heightMax; h += heightStep) {
for (int32_t w = widthMin; w <= widthMax; w += widthStep) {
valid.width = w;
valid.height = h;
{
SCOPED_TRACE("testWritableParam");
testWritableParam(&valid);
if (HasFailure()) {
printf("Failed while config width = %d, height = %d\n", valid.width,
valid.height);
}
if (HasFatalFailure()) return;
}
}
}
// TODO: validate possible values in C2InterfaceHelper is not implemented yet.
//// Test invalid values video size
//T invalid;
//// Width or height is smaller than min values
//if (!isUnderflowSubstract(widthMin, widthStep)) {
// invalid.width = widthMin - widthStep;
// invalid.height = heightMin;
// testInvalidWritableParam(&invalid);
//}
//if (!isUnderflowSubstract(heightMin, heightStep)) {
// invalid.width = widthMin;
// invalid.height = heightMin - heightStep;
// testInvalidWritableParam(&invalid);
//}
//// Width or height is bigger than max values
//if (!isOverflowAdd(widthMax, widthStep)) {
// invalid.width = widthMax + widthStep;
// invalid.height = heightMax;
// testInvalidWritableParam(&invalid);
//}
//if (!isOverflowAdd(heightMax, heightStep)) {
// invalid.width = widthMax;
// invalid.height = heightMax + heightStep;
// testInvalidWritableParam(&invalid);
//}
//// Invalid width/height within the range
//if (widthStep != 1) {
// invalid.width = widthMin + 1;
// invalid.height = heightMin;
// testInvalidWritableParam(&invalid);
//}
//if (heightStep != 1) {
// invalid.width = widthMin;
// invalid.height = heightMin + 1;
// testInvalidWritableParam(&invalid);
//}
}
#define TRACED_FAILURE(func) \
do { \
SCOPED_TRACE(#func); \
func; \
if (::testing::Test::HasFatalFailure()) return; \
} while (false)
TEST_F(C2VDACompIntfTest, CreateInstance) {
auto name = mIntf->getName();
auto id = mIntf->getId();
printf("name = %s\n", name.c_str());
printf("node_id = %u\n", id);
EXPECT_STREQ(name.c_str(), testCompName.c_str());
EXPECT_EQ(id, testCompNodeId);
}
TEST_F(C2VDACompIntfTest, TestInputFormat) {
C2StreamBufferTypeSetting::input expected(0u, C2FormatCompressed);
expected.setStream(0); // only support single stream
C2StreamBufferTypeSetting::input invalid(0u, C2FormatVideo);
invalid.setStream(0); // only support single stream
TRACED_FAILURE(testReadOnlyParam(&expected, &invalid));
}
TEST_F(C2VDACompIntfTest, TestOutputFormat) {
C2StreamBufferTypeSetting::output expected(0u, C2FormatVideo);
expected.setStream(0); // only support single stream
C2StreamBufferTypeSetting::output invalid(0u, C2FormatCompressed);
invalid.setStream(0); // only support single stream
TRACED_FAILURE(testReadOnlyParam(&expected, &invalid));
}
TEST_F(C2VDACompIntfTest, TestInputPortMime) {
std::shared_ptr<C2PortMediaTypeSetting::input> expected(
AllocSharedString<C2PortMediaTypeSetting::input>(MEDIA_MIMETYPE_VIDEO_AVC));
std::shared_ptr<C2PortMediaTypeSetting::input> invalid(
AllocSharedString<C2PortMediaTypeSetting::input>(MEDIA_MIMETYPE_VIDEO_RAW));
TRACED_FAILURE(testReadOnlyParamOnHeap(expected.get(), invalid.get()));
}
TEST_F(C2VDACompIntfTest, TestOutputPortMime) {
std::shared_ptr<C2PortMediaTypeSetting::output> expected(
AllocSharedString<C2PortMediaTypeSetting::output>(MEDIA_MIMETYPE_VIDEO_RAW));
std::shared_ptr<C2PortMediaTypeSetting::output> invalid(
AllocSharedString<C2PortMediaTypeSetting::output>(MEDIA_MIMETYPE_VIDEO_AVC));
TRACED_FAILURE(testReadOnlyParamOnHeap(expected.get(), invalid.get()));
}
TEST_F(C2VDACompIntfTest, TestVideoSize) {
C2StreamPictureSizeInfo::output videoSize;
videoSize.setStream(0); // only support single stream
std::vector<C2FieldSupportedValuesQuery> widthC2FSV = {
{C2ParamField(&videoSize, &C2StreamPictureSizeInfo::width),
C2FieldSupportedValuesQuery::CURRENT},
};
ASSERT_EQ(C2_OK, mIntf->querySupportedValues_vb(widthC2FSV, C2_DONT_BLOCK));
std::vector<C2FieldSupportedValuesQuery> heightC2FSV = {
{C2ParamField(&videoSize, &C2StreamPictureSizeInfo::height),
C2FieldSupportedValuesQuery::CURRENT},
};
ASSERT_EQ(C2_OK, mIntf->querySupportedValues_vb(heightC2FSV, C2_DONT_BLOCK));
ASSERT_EQ(1u, widthC2FSV.size());
ASSERT_EQ(C2_OK, widthC2FSV[0].status);
ASSERT_EQ(C2FieldSupportedValues::RANGE, widthC2FSV[0].values.type);
auto& widthFSVRange = widthC2FSV[0].values.range;
int32_t widthMin = widthFSVRange.min.i32;
int32_t widthMax = widthFSVRange.max.i32;
int32_t widthStep = widthFSVRange.step.i32;
ASSERT_EQ(1u, heightC2FSV.size());
ASSERT_EQ(C2_OK, heightC2FSV[0].status);
ASSERT_EQ(C2FieldSupportedValues::RANGE, heightC2FSV[0].values.type);
auto& heightFSVRange = heightC2FSV[0].values.range;
int32_t heightMin = heightFSVRange.min.i32;
int32_t heightMax = heightFSVRange.max.i32;
int32_t heightStep = heightFSVRange.step.i32;
// test updating valid and invalid values
TRACED_FAILURE(testWritableVideoSizeParam<C2StreamPictureSizeInfo::output>(
widthMin, widthMax, widthStep, heightMin, heightMax, heightStep));
}
TEST_F(C2VDACompIntfTest, TestInputAllocatorIds) {
std::shared_ptr<C2PortAllocatorsTuning::input> expected(
C2PortAllocatorsTuning::input::AllocShared(kInputAllocators));
std::shared_ptr<C2PortAllocatorsTuning::input> invalid(
C2PortAllocatorsTuning::input::AllocShared(kOutputAllocators));
TRACED_FAILURE(testReadOnlyParamOnHeap(expected.get(), invalid.get()));
}
TEST_F(C2VDACompIntfTest, TestOutputAllocatorIds) {
std::shared_ptr<C2PortAllocatorsTuning::output> expected(
C2PortAllocatorsTuning::output::AllocShared(kOutputAllocators));
std::shared_ptr<C2PortAllocatorsTuning::output> invalid(
C2PortAllocatorsTuning::output::AllocShared(kInputAllocators));
TRACED_FAILURE(testReadOnlyParamOnHeap(expected.get(), invalid.get()));
}
TEST_F(C2VDACompIntfTest, TestOutputBlockPoolIds) {
std::vector<std::unique_ptr<C2Param>> heapParams;
C2Param::Index index = C2PortBlockPoolsTuning::output::PARAM_TYPE;
// Query the param and check the default value.
ASSERT_EQ(C2_OK, mIntf->query_vb({}, {index}, C2_DONT_BLOCK, &heapParams));
ASSERT_EQ(1u, heapParams.size());
C2BlockPool::local_id_t value = ((C2PortBlockPoolsTuning*)heapParams[0].get())->m.values[0];
ASSERT_EQ(kDefaultOutputBlockPool, value);
// Configure the param.
C2BlockPool::local_id_t configBlockPools[] = {C2BlockPool::PLATFORM_START + 1};
std::shared_ptr<C2PortBlockPoolsTuning::output> newParam(
C2PortBlockPoolsTuning::output::AllocShared(configBlockPools));
std::vector<C2Param*> params{newParam.get()};
std::vector<std::unique_ptr<C2SettingResult>> failures;
ASSERT_EQ(C2_OK, mIntf->config_vb(params, C2_DONT_BLOCK, &failures));
EXPECT_EQ(0u, failures.size());
// Query the param again and check the value is as configured
heapParams.clear();
ASSERT_EQ(C2_OK, mIntf->query_vb({}, {index}, C2_DONT_BLOCK, &heapParams));
ASSERT_EQ(1u, heapParams.size());
value = ((C2PortBlockPoolsTuning*)heapParams[0].get())->m.values[0];
ASSERT_EQ(configBlockPools[0], value);
}
TEST_F(C2VDACompIntfTest, TestUnsupportedParam) {
C2ComponentTemporalInfo unsupportedParam;
std::vector<C2Param*> stackParams{&unsupportedParam};
ASSERT_EQ(C2_BAD_INDEX, mIntf->query_vb(stackParams, {}, C2_DONT_BLOCK, nullptr));
EXPECT_EQ(0u, unsupportedParam.size()); // invalidated
}
void dumpType(const C2FieldDescriptor::type_t type) {
switch (type) {
case C2FieldDescriptor::INT32:
printf("int32_t");
break;
case C2FieldDescriptor::UINT32:
printf("uint32_t");
break;
case C2FieldDescriptor::INT64:
printf("int64_t");
break;
case C2FieldDescriptor::UINT64:
printf("uint64_t");
break;
case C2FieldDescriptor::FLOAT:
printf("float");
break;
default:
printf("<flex>");
break;
}
}
void dumpStruct(const C2StructDescriptor& sd) {
printf(" struct: { ");
for (const C2FieldDescriptor& f : sd) {
printf("%s:", f.name().c_str());
dumpType(f.type());
printf(", ");
}
printf("}\n");
}
TEST_F(C2VDACompIntfTest, ParamReflector) {
std::vector<std::shared_ptr<C2ParamDescriptor>> params;
ASSERT_EQ(mIntf->querySupportedParams_nb(¶ms), C2_OK);
for (const auto& paramDesc : params) {
printf("name: %s\n", paramDesc->name().c_str());
printf(" required: %s\n", paramDesc->isRequired() ? "yes" : "no");
printf(" type: %x\n", paramDesc->index().type());
std::unique_ptr<C2StructDescriptor> desc{mReflector->describe(paramDesc->index().type())};
if (desc.get()) dumpStruct(*desc);
}
}
} // namespace android