// Copyright 2019 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. #ifndef ANDROID_C2_COMPONENT_INTERFACE_TEST_H #define ANDROID_C2_COMPONENT_INTERFACE_TEST_H #include #include #include #include #include #include #include namespace android { class C2CompIntfTest : public ::testing::Test { public: void dumpParamDescriptions(); // The following codes are template implicit instantiations. These codes should not be separated // into an individual cpp file from explicit specializations. Otherwise the compiler will fail // to instantiate templates when the explicit specialization is present. template void testReadOnlyParam(const T* expected, T* invalid) { testReadOnlyParamOnStack(expected, invalid); testReadOnlyParamOnHeap(expected, invalid); } template void checkReadOnlyFailureOnConfig(T* param) { std::vector params{param}; std::vector> 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); } // Note: this is not suitable for testing flex-type parameters. template void testReadOnlyParamOnStack(const T* expected, T* invalid) { T param; std::vector 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 void testReadOnlyParamOnHeap(const T* expected, T* invalid) { std::vector> 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 void testWritableParam(T* newParam) { std::vector params{newParam}; std::vector> 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 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> 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 void testInvalidWritableParam(T* invalidParam) { // Get the current parameter info T preParam; std::vector stackParams{&preParam}; ASSERT_EQ(C2_OK, mIntf->query_vb(stackParams, {}, C2_DONT_BLOCK, nullptr)); // Config invalid value. The failure is expected std::vector params{invalidParam}; std::vector> 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 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> 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::min(); } bool isOverflowAdd(int32_t a, int32_t b) { return a > 0 && b > std::numeric_limits::max() - a; } template void 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); //} } template void testWritableProfileLevelParam() { T info; std::vector profileC2FSV = { {C2ParamField(&info, &C2StreamProfileLevelInfo::profile), C2FieldSupportedValuesQuery::CURRENT}, }; ASSERT_EQ(C2_OK, mIntf->querySupportedValues_vb(profileC2FSV, C2_DONT_BLOCK)); ASSERT_EQ(1u, profileC2FSV.size()); ASSERT_EQ(C2_OK, profileC2FSV[0].status); ASSERT_EQ(C2FieldSupportedValues::VALUES, profileC2FSV[0].values.type); auto& profileFSVValues = profileC2FSV[0].values.values; std::vector levelC2FSV = { {C2ParamField(&info, &C2StreamProfileLevelInfo::level), C2FieldSupportedValuesQuery::CURRENT}, }; ASSERT_EQ(C2_OK, mIntf->querySupportedValues_vb(levelC2FSV, C2_DONT_BLOCK)); ASSERT_EQ(1u, levelC2FSV.size()); ASSERT_EQ(C2_OK, levelC2FSV[0].status); ASSERT_EQ(C2FieldSupportedValues::VALUES, levelC2FSV[0].values.type); auto& levelFSVValues = levelC2FSV[0].values.values; for (const auto& profile : profileFSVValues) { for (const auto& level : levelFSVValues) { info.profile = static_cast(profile.u32); info.level = static_cast(level.u32); { SCOPED_TRACE("testWritableParam"); testWritableParam(&info); if (HasFailure()) { printf("Failed while config profile = 0x%x, level = 0x%x\n", info.profile, info.level); } if (HasFatalFailure()) return; } } } // TODO: Add invalid value test after validate possible values in C2InterfaceHelper is // implemented. } protected: std::shared_ptr mIntf; std::shared_ptr mReflector; }; } // namespace android #endif // ANDROID_C2_COMPONENT_INTERFACE_TEST_H