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 /* Time_Out for start(), stop(), reset(), release(), flush(), queue() are
30  * defined in hardware/interfaces/media/c2/1.0/IComponent.hal. Adding 50ms
31  * extra in case of timeout is 500ms, 1ms extra in case timeout is 1ms/5ms. All
32  * timeout is calculated in us.
33  */
34 #define START_TIME_OUT                  550000
35 #define STOP_TIME_OUT                   550000
36 #define RESET_TIME_OUT                  550000
37 #define RELEASE_TIME_OUT                550000
38 #define FLUSH_TIME_OUT                  6000
39 #define QUEUE_TIME_OUT                  2000
40 
41 // Time_Out for config(), query(), querySupportedParams() are defined in
42 // hardware/interfaces/media/c2/1.0/IConfigurable.hal.
43 #define CONFIG_TIME_OUT                 6000
44 #define QUERY_TIME_OUT                  6000
45 #define QUERYSUPPORTEDPARAMS_TIME_OUT   2000
46 
47 #define CHECK_TIMEOUT(timeConsumed, TIME_OUT, FuncName)          \
48     if (timeConsumed > TIME_OUT) {                               \
49         ALOGW(                                                   \
50             "TIMED_OUT %s  timeConsumed=%" PRId64 " us is "      \
51             "greater than threshold %d us",                      \
52             FuncName, timeConsumed, TIME_OUT);                   \
53     }
54 
55 static ComponentTestEnvironment* gEnv = nullptr;
56 
57 namespace {
58 
59 // google.codec2 Component test setup
60 class Codec2ComponentHidlTest : public ::testing::VtsHalHidlTargetTestBase {
61    private:
62     typedef ::testing::VtsHalHidlTargetTestBase Super;
63 
64    public:
SetUp()65     virtual void SetUp() override {
66         Super::SetUp();
67         mEos = false;
68         mClient = android::Codec2Client::CreateFromService(
69             gEnv->getInstance().c_str());
70         ASSERT_NE(mClient, nullptr);
71         mListener.reset(new CodecListener(
72             [this](std::list<std::unique_ptr<C2Work>>& workItems) {
73                 handleWorkDone(workItems);
74             }));
75         ASSERT_NE(mListener, nullptr);
76         mClient->createComponent(gEnv->getComponent().c_str(), mListener,
77                                  &mComponent);
78         ASSERT_NE(mComponent, nullptr);
79         for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
80             mWorkQueue.emplace_back(new C2Work);
81         }
82     }
83 
TearDown()84     virtual void TearDown() override {
85         if (mComponent != nullptr) {
86             // If you have encountered a fatal failure, it is possible that
87             // freeNode() will not go through. Instead of hanging the app.
88             // let it pass through and report errors
89             if (::testing::Test::HasFatalFailure()) return;
90             mComponent->release();
91             mComponent = nullptr;
92         }
93         Super::TearDown();
94     }
95     // callback function to process onWorkDone received by Listener
handleWorkDone(std::list<std::unique_ptr<C2Work>> & workItems)96     void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
97         for (std::unique_ptr<C2Work>& work : workItems) {
98             if (!work->worklets.empty()) {
99                 bool mCsd = false;
100                 uint32_t mFramesReceived = 0;
101                 std::list<uint64_t> mFlushedIndices;
102                 workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition,
103                          mWorkQueue, mEos, mCsd, mFramesReceived);
104             }
105         }
106     }
107 
108     bool mEos;
109     std::mutex mQueueLock;
110     std::condition_variable mQueueCondition;
111     std::list<std::unique_ptr<C2Work>> mWorkQueue;
112 
113     std::shared_ptr<android::Codec2Client> mClient;
114     std::shared_ptr<android::Codec2Client::Listener> mListener;
115     std::shared_ptr<android::Codec2Client::Component> mComponent;
116 
117    protected:
description(const std::string & description)118     static void description(const std::string& description) {
119         RecordProperty("description", description);
120     }
121 };
122 
123 // Test Empty Flush
TEST_F(Codec2ComponentHidlTest,EmptyFlush)124 TEST_F(Codec2ComponentHidlTest, EmptyFlush) {
125     ALOGV("Empty Flush Test");
126     c2_status_t err = mComponent->start();
127     ASSERT_EQ(err, C2_OK);
128 
129     std::list<std::unique_ptr<C2Work>> flushedWork;
130     err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
131     ASSERT_EQ(err, C2_OK);
132 
133     err = mComponent->stop();
134     ASSERT_EQ(err, C2_OK);
135     // Empty Flush should not return any work
136     ASSERT_EQ(flushedWork.size(), 0u);
137 }
138 
139 // Test Queue Empty Work
TEST_F(Codec2ComponentHidlTest,QueueEmptyWork)140 TEST_F(Codec2ComponentHidlTest, QueueEmptyWork) {
141     ALOGV("Queue Empty Work Test");
142     c2_status_t err = mComponent->start();
143     ASSERT_EQ(err, C2_OK);
144 
145     // Queueing an empty WorkBundle
146     std::list<std::unique_ptr<C2Work>> workList;
147     mComponent->queue(&workList);
148 
149     err = mComponent->reset();
150     ASSERT_EQ(err, C2_OK);
151 }
152 
153 // Test Component Configuration
TEST_F(Codec2ComponentHidlTest,Config)154 TEST_F(Codec2ComponentHidlTest, Config) {
155     ALOGV("Configuration Test");
156 
157     C2String name = mComponent->getName();
158     EXPECT_NE(name.empty(), true) << "Invalid Component Name";
159 
160     c2_status_t err = C2_OK;
161     std::vector<std::unique_ptr<C2Param>> queried;
162     std::vector<std::unique_ptr<C2SettingResult>> failures;
163 
164     // Query supported params by the component
165     std::vector<std::shared_ptr<C2ParamDescriptor>> params;
166     err = mComponent->querySupportedParams(&params);
167     ASSERT_EQ(err, C2_OK);
168     ALOGV("Number of total params - %zu", params.size());
169 
170     // Query and config all the supported params
171     for (std::shared_ptr<C2ParamDescriptor> p : params) {
172         ALOGD("Querying index %d", (int)p->index());
173         err = mComponent->query({}, {p->index()}, C2_DONT_BLOCK, &queried);
174         EXPECT_NE(queried.size(), 0u);
175         EXPECT_EQ(err, C2_OK);
176         err = mComponent->config({queried[0].get()}, C2_DONT_BLOCK, &failures);
177         ASSERT_EQ(err, C2_OK);
178         ASSERT_EQ(failures.size(), 0u);
179     }
180 }
181 
182 // Test Multiple Start Stop Reset Test
TEST_F(Codec2ComponentHidlTest,MultipleStartStopReset)183 TEST_F(Codec2ComponentHidlTest, MultipleStartStopReset) {
184     ALOGV("Multiple Start Stop and Reset Test");
185 
186     for (size_t i = 0; i < MAX_RETRY; i++) {
187         mComponent->start();
188         mComponent->stop();
189     }
190 
191     ASSERT_EQ(mComponent->start(), C2_OK);
192 
193     for (size_t i = 0; i < MAX_RETRY; i++) {
194         mComponent->reset();
195     }
196 
197     ASSERT_EQ(mComponent->start(), C2_OK);
198     ASSERT_EQ(mComponent->stop(), C2_OK);
199 
200     // Second stop should return error
201     ASSERT_NE(mComponent->stop(), C2_OK);
202 }
203 
204 // Test Component Release API
TEST_F(Codec2ComponentHidlTest,MultipleRelease)205 TEST_F(Codec2ComponentHidlTest, MultipleRelease) {
206     ALOGV("Multiple Release Test");
207     c2_status_t err = mComponent->start();
208     ASSERT_EQ(err, C2_OK);
209 
210     // Query Component Domain Type
211     std::vector<std::unique_ptr<C2Param>> queried;
212     err = mComponent->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
213                             C2_DONT_BLOCK, &queried);
214     EXPECT_NE(queried.size(), 0u);
215 
216     // Configure Component Domain
217     std::vector<std::unique_ptr<C2SettingResult>> failures;
218     C2PortMediaTypeSetting::input* portMediaType =
219         C2PortMediaTypeSetting::input::From(queried[0].get());
220     err = mComponent->config({portMediaType}, C2_DONT_BLOCK, &failures);
221     ASSERT_EQ(err, C2_OK);
222     ASSERT_EQ(failures.size(), 0u);
223 
224     for (size_t i = 0; i < MAX_RETRY; i++) {
225         mComponent->release();
226     }
227 }
228 
229 class Codec2ComponentInputTests : public Codec2ComponentHidlTest,
230         public ::testing::WithParamInterface<std::pair<uint32_t, bool> > {
231 };
232 
TEST_P(Codec2ComponentInputTests,InputBufferTest)233 TEST_P(Codec2ComponentInputTests, InputBufferTest) {
234     description("Tests for different inputs");
235 
236     uint32_t flags = GetParam().first;
237     bool isNullBuffer = GetParam().second;
238     if (isNullBuffer) ALOGD("Testing for null input buffer with flag : %u", flags);
239     else ALOGD("Testing for empty input buffer with flag : %u", flags);
240     mEos = false;
241     ASSERT_EQ(mComponent->start(), C2_OK);
242     ASSERT_NO_FATAL_FAILURE(testInputBuffer(
243         mComponent, mQueueLock, mWorkQueue, flags, isNullBuffer));
244 
245     ALOGD("Waiting for input consumption");
246     ASSERT_NO_FATAL_FAILURE(
247         waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
248 
249     if (flags == C2FrameData::FLAG_END_OF_STREAM) ASSERT_EQ(mEos, true);
250     ASSERT_EQ(mComponent->stop(), C2_OK);
251     ASSERT_EQ(mComponent->reset(), C2_OK);
252 }
253 
254 INSTANTIATE_TEST_CASE_P(NonStdInputs, Codec2ComponentInputTests, ::testing::Values(
255     std::make_pair(0, true),
256     std::make_pair(C2FrameData::FLAG_END_OF_STREAM, true),
257     std::make_pair(0, false),
258     std::make_pair(C2FrameData::FLAG_CODEC_CONFIG, false),
259     std::make_pair(C2FrameData::FLAG_END_OF_STREAM, false)));
260 
261 // Test API's Timeout
TEST_F(Codec2ComponentHidlTest,Timeout)262 TEST_F(Codec2ComponentHidlTest, Timeout) {
263     ALOGV("Timeout Test");
264     c2_status_t err = C2_OK;
265 
266     int64_t startTime = getNowUs();
267     err = mComponent->start();
268     int64_t timeConsumed = getNowUs() - startTime;
269     CHECK_TIMEOUT(timeConsumed, START_TIME_OUT, "start()");
270     ALOGV("mComponent->start() timeConsumed=%" PRId64 " us", timeConsumed);
271     ASSERT_EQ(err, C2_OK);
272 
273     startTime = getNowUs();
274     err = mComponent->reset();
275     timeConsumed = getNowUs() - startTime;
276     CHECK_TIMEOUT(timeConsumed, RESET_TIME_OUT, "reset()");
277     ALOGV("mComponent->reset() timeConsumed=%" PRId64 " us", timeConsumed);
278     ASSERT_EQ(err, C2_OK);
279 
280     err = mComponent->start();
281     ASSERT_EQ(err, C2_OK);
282 
283     // Query supported params by the component
284     std::vector<std::shared_ptr<C2ParamDescriptor>> params;
285     startTime = getNowUs();
286     err = mComponent->querySupportedParams(&params);
287     timeConsumed = getNowUs() - startTime;
288     CHECK_TIMEOUT(timeConsumed, QUERYSUPPORTEDPARAMS_TIME_OUT,
289                   "querySupportedParams()");
290     ALOGV("mComponent->querySupportedParams() timeConsumed=%" PRId64 " us",
291           timeConsumed);
292     ASSERT_EQ(err, C2_OK);
293 
294     std::vector<std::unique_ptr<C2Param>> queried;
295     std::vector<std::unique_ptr<C2SettingResult>> failures;
296     // Query and config all the supported params
297     for (std::shared_ptr<C2ParamDescriptor> p : params) {
298         startTime = getNowUs();
299         err = mComponent->query({}, {p->index()}, C2_DONT_BLOCK, &queried);
300         timeConsumed = getNowUs() - startTime;
301         CHECK_TIMEOUT(timeConsumed, QUERY_TIME_OUT, "query()");
302         EXPECT_NE(queried.size(), 0u);
303         EXPECT_EQ(err, C2_OK);
304         ALOGV("mComponent->query() for %s timeConsumed=%" PRId64 " us",
305               p->name().c_str(), timeConsumed);
306 
307         startTime = getNowUs();
308         err = mComponent->config({queried[0].get()}, C2_DONT_BLOCK, &failures);
309         timeConsumed = getNowUs() - startTime;
310         CHECK_TIMEOUT(timeConsumed, CONFIG_TIME_OUT, "config()");
311         ASSERT_EQ(err, C2_OK);
312         ASSERT_EQ(failures.size(), 0u);
313         ALOGV("mComponent->config() for %s timeConsumed=%" PRId64 " us",
314               p->name().c_str(), timeConsumed);
315     }
316 
317     std::list<std::unique_ptr<C2Work>> workList;
318     startTime = getNowUs();
319     err = mComponent->queue(&workList);
320     timeConsumed = getNowUs() - startTime;
321     ALOGV("mComponent->queue() timeConsumed=%" PRId64 " us", timeConsumed);
322     CHECK_TIMEOUT(timeConsumed, QUEUE_TIME_OUT, "queue()");
323 
324     startTime = getNowUs();
325     err = mComponent->flush(C2Component::FLUSH_COMPONENT, &workList);
326     timeConsumed = getNowUs() - startTime;
327     ALOGV("mComponent->flush() timeConsumed=%" PRId64 " us", timeConsumed);
328     CHECK_TIMEOUT(timeConsumed, FLUSH_TIME_OUT, "flush()");
329 
330     startTime = getNowUs();
331     err = mComponent->stop();
332     timeConsumed = getNowUs() - startTime;
333     ALOGV("mComponent->stop() timeConsumed=%" PRId64 " us", timeConsumed);
334     CHECK_TIMEOUT(timeConsumed, STOP_TIME_OUT, "stop()");
335     ASSERT_EQ(err, C2_OK);
336 
337     startTime = getNowUs();
338     err = mComponent->release();
339     timeConsumed = getNowUs() - startTime;
340     ALOGV("mComponent->release() timeConsumed=%" PRId64 " us", timeConsumed);
341     CHECK_TIMEOUT(timeConsumed, RELEASE_TIME_OUT, "release()");
342     ASSERT_EQ(err, C2_OK);
343 
344 }
345 
346 }  // anonymous namespace
347 
348 // TODO: Add test for Invalid work,
349 // TODO: Add test for Invalid states
main(int argc,char ** argv)350 int main(int argc, char** argv) {
351     gEnv = new ComponentTestEnvironment();
352     ::testing::AddGlobalTestEnvironment(gEnv);
353     ::testing::InitGoogleTest(&argc, argv);
354     gEnv->init(&argc, argv);
355     int status = gEnv->initFromOptions(argc, argv);
356     if (status == 0) {
357         status = RUN_ALL_TESTS();
358         LOG(INFO) << "C2 Test result = " << status;
359     }
360     return status;
361 }
362