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