1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "codec2_hidl_hal_component_test"
19 
20 #include <android-base/logging.h>
21 #include <gtest/gtest.h>
22 
23 #include <C2Config.h>
24 #include <codec2/hidl/client.h>
25 
26 #include <VtsHalHidlTargetTestBase.h>
27 #include "media_c2_hidl_test_common.h"
28 
29 static ComponentTestEnvironment* gEnv = nullptr;
30 
31 namespace {
32 
33 // google.codec2 Component test setup
34 class Codec2ComponentHidlTest : public ::testing::VtsHalHidlTargetTestBase {
35    private:
36     typedef ::testing::VtsHalHidlTargetTestBase Super;
37 
38    public:
SetUp()39     virtual void SetUp() override {
40         Super::SetUp();
41         mEos = false;
42         mClient = android::Codec2Client::CreateFromService(
43             gEnv->getInstance().c_str());
44         ASSERT_NE(mClient, nullptr);
45         mListener.reset(new CodecListener(
46             [this](std::list<std::unique_ptr<C2Work>>& workItems) {
47                 handleWorkDone(workItems);
48             }));
49         ASSERT_NE(mListener, nullptr);
50         mClient->createComponent(gEnv->getComponent().c_str(), mListener,
51                                  &mComponent);
52         ASSERT_NE(mComponent, nullptr);
53         for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
54             mWorkQueue.emplace_back(new C2Work);
55         }
56     }
57 
TearDown()58     virtual void TearDown() override {
59         if (mComponent != nullptr) {
60             // If you have encountered a fatal failure, it is possible that
61             // freeNode() will not go through. Instead of hanging the app.
62             // let it pass through and report errors
63             if (::testing::Test::HasFatalFailure()) return;
64             mComponent->release();
65             mComponent = nullptr;
66         }
67         Super::TearDown();
68     }
69     // callback function to process onWorkDone received by Listener
handleWorkDone(std::list<std::unique_ptr<C2Work>> & workItems)70     void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
71         for (std::unique_ptr<C2Work>& work : workItems) {
72             if (!work->worklets.empty()) {
73                 bool mCsd = false;
74                 uint32_t mFramesReceived = 0;
75                 std::list<uint64_t> mFlushedIndices;
76                 workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition,
77                          mWorkQueue, mEos, mCsd, mFramesReceived);
78             }
79         }
80     }
81 
82     bool mEos;
83     std::mutex mQueueLock;
84     std::condition_variable mQueueCondition;
85     std::list<std::unique_ptr<C2Work>> mWorkQueue;
86 
87     std::shared_ptr<android::Codec2Client> mClient;
88     std::shared_ptr<android::Codec2Client::Listener> mListener;
89     std::shared_ptr<android::Codec2Client::Component> mComponent;
90 
91    protected:
description(const std::string & description)92     static void description(const std::string& description) {
93         RecordProperty("description", description);
94     }
95 };
96 
97 // Test Empty Flush
TEST_F(Codec2ComponentHidlTest,EmptyFlush)98 TEST_F(Codec2ComponentHidlTest, EmptyFlush) {
99     ALOGV("Empty Flush Test");
100     c2_status_t err = mComponent->start();
101     ASSERT_EQ(err, C2_OK);
102 
103     std::list<std::unique_ptr<C2Work>> flushedWork;
104     err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
105     ASSERT_EQ(err, C2_OK);
106 
107     err = mComponent->stop();
108     ASSERT_EQ(err, C2_OK);
109     // Empty Flush should not return any work
110     ASSERT_EQ(flushedWork.size(), 0u);
111 }
112 
113 // Test Queue Empty Work
TEST_F(Codec2ComponentHidlTest,QueueEmptyWork)114 TEST_F(Codec2ComponentHidlTest, QueueEmptyWork) {
115     ALOGV("Queue Empty Work Test");
116     c2_status_t err = mComponent->start();
117     ASSERT_EQ(err, C2_OK);
118 
119     // Queueing an empty WorkBundle
120     std::list<std::unique_ptr<C2Work>> workList;
121     err = mComponent->queue(&workList);
122     ASSERT_EQ(err, C2_OK);
123 
124     err = mComponent->reset();
125     ASSERT_EQ(err, C2_OK);
126 }
127 
128 // Test Component Configuration
TEST_F(Codec2ComponentHidlTest,Config)129 TEST_F(Codec2ComponentHidlTest, Config) {
130     ALOGV("Configuration Test");
131 
132     C2String name = mComponent->getName();
133     EXPECT_NE(name.empty(), true) << "Invalid Component Name";
134 
135     c2_status_t err = C2_OK;
136     std::vector<std::unique_ptr<C2Param>> queried;
137     std::vector<std::unique_ptr<C2SettingResult>> failures;
138 
139     // Query supported params by the component
140     std::vector<std::shared_ptr<C2ParamDescriptor>> params;
141     err = mComponent->querySupportedParams(&params);
142     ASSERT_EQ(err, C2_OK);
143     ALOGV("Number of total params - %zu", params.size());
144 
145     // Query and config all the supported params
146     for (std::shared_ptr<C2ParamDescriptor> p : params) {
147         ALOGD("Querying index %d", (int)p->index());
148         err = mComponent->query({}, {p->index()}, C2_DONT_BLOCK, &queried);
149         EXPECT_NE(queried.size(), 0u);
150         EXPECT_EQ(err, C2_OK);
151         err = mComponent->config({queried[0].get()}, C2_DONT_BLOCK, &failures);
152         ASSERT_EQ(err, C2_OK);
153         ASSERT_EQ(failures.size(), 0u);
154     }
155 }
156 
157 // Test Multiple Start Stop Reset Test
TEST_F(Codec2ComponentHidlTest,MultipleStartStopReset)158 TEST_F(Codec2ComponentHidlTest, MultipleStartStopReset) {
159     ALOGV("Multiple Start Stop and Reset Test");
160     c2_status_t err = C2_OK;
161 
162     for (size_t i = 0; i < MAX_RETRY; i++) {
163         err = mComponent->start();
164         ASSERT_EQ(err, C2_OK);
165 
166         err = mComponent->stop();
167         ASSERT_EQ(err, C2_OK);
168     }
169 
170     err = mComponent->start();
171     ASSERT_EQ(err, C2_OK);
172 
173     for (size_t i = 0; i < MAX_RETRY; i++) {
174         err = mComponent->reset();
175         ASSERT_EQ(err, C2_OK);
176     }
177 
178     err = mComponent->start();
179     ASSERT_EQ(err, C2_OK);
180 
181     err = mComponent->stop();
182     ASSERT_EQ(err, C2_OK);
183 
184     // Second stop should return error
185     err = mComponent->stop();
186     ASSERT_NE(err, C2_OK);
187 }
188 
189 // Test Component Release API
TEST_F(Codec2ComponentHidlTest,MultipleRelease)190 TEST_F(Codec2ComponentHidlTest, MultipleRelease) {
191     ALOGV("Multiple Release Test");
192     c2_status_t err = mComponent->start();
193     ASSERT_EQ(err, C2_OK);
194 
195     // Query Component Domain Type
196     std::vector<std::unique_ptr<C2Param>> queried;
197     err = mComponent->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
198                             C2_DONT_BLOCK, &queried);
199     EXPECT_NE(queried.size(), 0u);
200 
201     // Configure Component Domain
202     std::vector<std::unique_ptr<C2SettingResult>> failures;
203     C2PortMediaTypeSetting::input* portMediaType =
204         C2PortMediaTypeSetting::input::From(queried[0].get());
205     err = mComponent->config({portMediaType}, C2_DONT_BLOCK, &failures);
206     ASSERT_EQ(err, C2_OK);
207     ASSERT_EQ(failures.size(), 0u);
208 
209     for (size_t i = 0; i < MAX_RETRY; i++) {
210         err = mComponent->release();
211         ASSERT_EQ(err, C2_OK);
212     }
213 }
214 
215 class Codec2ComponentInputTests : public Codec2ComponentHidlTest,
216         public ::testing::WithParamInterface<std::pair<uint32_t, bool> > {
217 };
218 
TEST_P(Codec2ComponentInputTests,InputBufferTest)219 TEST_P(Codec2ComponentInputTests, InputBufferTest) {
220     description("Tests for different inputs");
221 
222     uint32_t flags = GetParam().first;
223     bool isNullBuffer = GetParam().second;
224     if (isNullBuffer) ALOGD("Testing for null input buffer with flag : %u", flags);
225     else ALOGD("Testing for empty input buffer with flag : %u", flags);
226     mEos = false;
227     ASSERT_EQ(mComponent->start(), C2_OK);
228     ASSERT_NO_FATAL_FAILURE(testInputBuffer(
229         mComponent, mQueueLock, mWorkQueue, flags, isNullBuffer));
230 
231     ALOGD("Waiting for input consumption");
232     ASSERT_NO_FATAL_FAILURE(
233         waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
234 
235     if (flags == C2FrameData::FLAG_END_OF_STREAM) ASSERT_EQ(mEos, true);
236     ASSERT_EQ(mComponent->stop(), C2_OK);
237     ASSERT_EQ(mComponent->reset(), C2_OK);
238 }
239 
240 INSTANTIATE_TEST_CASE_P(NonStdInputs, Codec2ComponentInputTests, ::testing::Values(
241     std::make_pair(0, true),
242     std::make_pair(C2FrameData::FLAG_END_OF_STREAM, true),
243     std::make_pair(0, false),
244     std::make_pair(C2FrameData::FLAG_CODEC_CONFIG, false),
245     std::make_pair(C2FrameData::FLAG_END_OF_STREAM, false)));
246 
247 }  // anonymous namespace
248 
249 // TODO: Add test for Invalid work,
250 // TODO: Add test for Invalid states
main(int argc,char ** argv)251 int main(int argc, char** argv) {
252     gEnv = new ComponentTestEnvironment();
253     ::testing::AddGlobalTestEnvironment(gEnv);
254     ::testing::InitGoogleTest(&argc, argv);
255     gEnv->init(&argc, argv);
256     int status = gEnv->initFromOptions(argc, argv);
257     if (status == 0) {
258         status = RUN_ALL_TESTS();
259         LOG(INFO) << "C2 Test result = " << status;
260     }
261     return status;
262 }
263