• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_audio_enc_test"
19 
20 #include <android-base/logging.h>
21 #include <gtest/gtest.h>
22 #include <stdio.h>
23 #include <fstream>
24 #include <algorithm>
25 
26 #include <codec2/hidl/client.h>
27 #include <C2AllocatorIon.h>
28 #include <C2Config.h>
29 #include <C2Debug.h>
30 #include <C2Buffer.h>
31 #include <C2BufferPriv.h>
32 
33 using android::C2AllocatorIon;
34 
35 #include <VtsHalHidlTargetTestBase.h>
36 #include "media_c2_audio_hidl_test_common.h"
37 #include "media_c2_hidl_test_common.h"
38 
39 class LinearBuffer : public C2Buffer {
40    public:
LinearBuffer(const std::shared_ptr<C2LinearBlock> & block)41     explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
42         : C2Buffer(
43               {block->share(block->offset(), block->size(), ::C2Fence())}) {}
44 };
45 
46 static ComponentTestEnvironment* gEnv = nullptr;
47 
48 namespace {
49 
50 class Codec2AudioEncHidlTest : public ::testing::VtsHalHidlTargetTestBase {
51    private:
52     typedef ::testing::VtsHalHidlTargetTestBase Super;
53 
54    public:
getTestCaseInfo() const55     ::std::string getTestCaseInfo() const override {
56         return ::std::string() +
57                 "Component: " + gEnv->getComponent().c_str() + " | " +
58                 "Instance: " + gEnv->getInstance().c_str() + " | " +
59                 "Res: " + gEnv->getRes().c_str();
60     }
61 
62     // google.codec2 Audio test setup
SetUp()63     virtual void SetUp() override {
64         Super::SetUp();
65         mDisableTest = false;
66         ALOGV("Codec2AudioEncHidlTest SetUp");
67         mClient = android::Codec2Client::CreateFromService(
68             gEnv->getInstance().c_str());
69         ASSERT_NE(mClient, nullptr);
70         mListener.reset(new CodecListener(
71             [this](std::list<std::unique_ptr<C2Work>>& workItems) {
72                 handleWorkDone(workItems);
73             }));
74         ASSERT_NE(mListener, nullptr);
75         for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
76             mWorkQueue.emplace_back(new C2Work);
77         }
78         mClient->createComponent(gEnv->getComponent().c_str(), mListener,
79                                  &mComponent);
80         ASSERT_NE(mComponent, nullptr);
81 
82         std::shared_ptr<C2AllocatorStore> store =
83             android::GetCodec2PlatformAllocatorStore();
84         CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR,
85                                        &mLinearAllocator),
86                  C2_OK);
87         mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator,
88                                                           mBlockPoolId++);
89         ASSERT_NE(mLinearPool, nullptr);
90 
91         mCompName = unknown_comp;
92         struct StringToName {
93             const char* Name;
94             standardComp CompName;
95         };
96         const StringToName kStringToName[] = {
97             {"aac", aac},
98             {"flac", flac},
99             {"opus", opus},
100             {"amrnb", amrnb},
101             {"amrwb", amrwb},
102         };
103         const size_t kNumStringToName =
104             sizeof(kStringToName) / sizeof(kStringToName[0]);
105 
106         std::string substring;
107         std::string comp;
108         substring = std::string(gEnv->getComponent());
109         /* TODO: better approach to find the component */
110         /* "c2.android." => 11th position */
111         size_t pos = 11;
112         size_t len = substring.find(".encoder", pos);
113         comp = substring.substr(pos, len - pos);
114 
115         for (size_t i = 0; i < kNumStringToName; ++i) {
116             if (!strcasecmp(comp.c_str(), kStringToName[i].Name)) {
117                 mCompName = kStringToName[i].CompName;
118                 break;
119             }
120         }
121         mEos = false;
122         mCsd = false;
123         mFramesReceived = 0;
124         if (mCompName == unknown_comp) mDisableTest = true;
125         if (mDisableTest) std::cout << "[   WARN   ] Test Disabled \n";
126     }
127 
TearDown()128     virtual void TearDown() override {
129         if (mComponent != nullptr) {
130             if (::testing::Test::HasFatalFailure()) return;
131             mComponent->release();
132             mComponent = nullptr;
133         }
134         Super::TearDown();
135     }
136     // callback function to process onWorkDone received by Listener
handleWorkDone(std::list<std::unique_ptr<C2Work>> & workItems)137     void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
138         for (std::unique_ptr<C2Work>& work : workItems) {
139             if (!work->worklets.empty()) {
140                 workDone(mComponent, work, mFlushedIndices, mQueueLock,
141                          mQueueCondition, mWorkQueue, mEos, mCsd,
142                          mFramesReceived);
143             }
144         }
145     }
146     enum standardComp {
147         aac,
148         flac,
149         opus,
150         amrnb,
151         amrwb,
152         unknown_comp,
153     };
154 
155     bool mEos;
156     bool mCsd;
157     bool mDisableTest;
158     standardComp mCompName;
159     uint32_t mFramesReceived;
160     std::list<uint64_t> mFlushedIndices;
161 
162     C2BlockPool::local_id_t mBlockPoolId;
163     std::shared_ptr<C2BlockPool> mLinearPool;
164     std::shared_ptr<C2Allocator> mLinearAllocator;
165 
166     std::mutex mQueueLock;
167     std::condition_variable mQueueCondition;
168     std::list<std::unique_ptr<C2Work>> mWorkQueue;
169 
170     std::shared_ptr<android::Codec2Client> mClient;
171     std::shared_ptr<android::Codec2Client::Listener> mListener;
172     std::shared_ptr<android::Codec2Client::Component> mComponent;
173 
174    protected:
description(const std::string & description)175     static void description(const std::string& description) {
176         RecordProperty("description", description);
177     }
178 };
179 
validateComponent(const std::shared_ptr<android::Codec2Client::Component> & component,Codec2AudioEncHidlTest::standardComp compName,bool & disableTest)180 void validateComponent(
181     const std::shared_ptr<android::Codec2Client::Component>& component,
182     Codec2AudioEncHidlTest::standardComp compName, bool& disableTest) {
183     // Validate its a C2 Component
184     if (component->getName().find("c2") == std::string::npos) {
185         ALOGE("Not a c2 component");
186         disableTest = true;
187         return;
188     }
189 
190     // Validate its not an encoder and the component to be tested is audio
191     if (component->getName().find("decoder") != std::string::npos) {
192         ALOGE("Expected Encoder, given Decoder");
193         disableTest = true;
194         return;
195     }
196     std::vector<std::unique_ptr<C2Param>> queried;
197     c2_status_t c2err =
198         component->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
199                          C2_DONT_BLOCK, &queried);
200     if (c2err != C2_OK && queried.size() == 0) {
201         ALOGE("Query media type failed => %d", c2err);
202     } else {
203         std::string inputDomain =
204             ((C2StreamMediaTypeSetting::input*)queried[0].get())->m.value;
205         if (inputDomain.find("audio/") == std::string::npos) {
206             ALOGE("Expected Audio Component");
207             disableTest = true;
208             return;
209         }
210     }
211 
212     // Validates component name
213     if (compName == Codec2AudioEncHidlTest::unknown_comp) {
214         ALOGE("Component InValid");
215         disableTest = true;
216         return;
217     }
218     ALOGV("Component Valid");
219 }
220 
221 // Set Default config param.
setupConfigParam(const std::shared_ptr<android::Codec2Client::Component> & component,int32_t nChannels,int32_t nSampleRate)222 void setupConfigParam(
223     const std::shared_ptr<android::Codec2Client::Component>& component,
224     int32_t nChannels, int32_t nSampleRate) {
225     std::vector<std::unique_ptr<C2SettingResult>> failures;
226     C2StreamSampleRateInfo::input sampleRateInfo(0u, nSampleRate);
227     C2StreamChannelCountInfo::input channelCountInfo(0u, nChannels);
228 
229     std::vector<C2Param*> configParam{&sampleRateInfo, &channelCountInfo};
230     c2_status_t status =
231         component->config(configParam, C2_DONT_BLOCK, &failures);
232     ASSERT_EQ(failures.size(), 0u);
233     ASSERT_EQ(status, C2_OK);
234 }
235 
236 // LookUpTable of clips and metadata for component testing
GetURLForComponent(Codec2AudioEncHidlTest::standardComp comp,char * mURL)237 void GetURLForComponent(Codec2AudioEncHidlTest::standardComp comp, char* mURL) {
238     struct CompToURL {
239         Codec2AudioEncHidlTest::standardComp comp;
240         const char* mURL;
241     };
242     static const CompToURL kCompToURL[] = {
243         {Codec2AudioEncHidlTest::standardComp::aac,
244          "bbb_raw_2ch_48khz_s16le.raw"},
245         {Codec2AudioEncHidlTest::standardComp::amrnb,
246          "bbb_raw_1ch_8khz_s16le.raw"},
247         {Codec2AudioEncHidlTest::standardComp::amrwb,
248          "bbb_raw_1ch_16khz_s16le.raw"},
249         {Codec2AudioEncHidlTest::standardComp::flac,
250          "bbb_raw_2ch_48khz_s16le.raw"},
251         {Codec2AudioEncHidlTest::standardComp::opus,
252          "bbb_raw_2ch_48khz_s16le.raw"},
253     };
254 
255     for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
256         if (kCompToURL[i].comp == comp) {
257             strcat(mURL, kCompToURL[i].mURL);
258             return;
259         }
260     }
261 }
262 
encodeNFrames(const std::shared_ptr<android::Codec2Client::Component> & component,std::mutex & queueLock,std::condition_variable & queueCondition,std::list<std::unique_ptr<C2Work>> & workQueue,std::list<uint64_t> & flushedIndices,std::shared_ptr<C2BlockPool> & linearPool,std::ifstream & eleStream,uint32_t nFrames,int32_t samplesPerFrame,int32_t nChannels,int32_t nSampleRate,bool flushed=false,bool signalEOS=true)263 void encodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
264                    std::mutex &queueLock, std::condition_variable& queueCondition,
265                    std::list<std::unique_ptr<C2Work>>& workQueue,
266                    std::list<uint64_t>& flushedIndices,
267                    std::shared_ptr<C2BlockPool>& linearPool,
268                    std::ifstream& eleStream, uint32_t nFrames,
269                    int32_t samplesPerFrame, int32_t nChannels,
270                    int32_t nSampleRate, bool flushed = false,
271                    bool signalEOS = true) {
272     typedef std::unique_lock<std::mutex> ULock;
273 
274     uint32_t frameID = 0;
275     uint32_t maxRetry = 0;
276     int bytesCount = samplesPerFrame * nChannels * 2;
277     int32_t timestampIncr =
278         (int)(((float)samplesPerFrame / nSampleRate) * 1000000);
279     uint64_t timestamp = 0;
280     while (1) {
281         if (nFrames == 0) break;
282         uint32_t flags = 0;
283         std::unique_ptr<C2Work> work;
284         // Prepare C2Work
285         while (!work && (maxRetry < MAX_RETRY)) {
286             ULock l(queueLock);
287             if (!workQueue.empty()) {
288                 work.swap(workQueue.front());
289                 workQueue.pop_front();
290             } else {
291                 queueCondition.wait_for(l, TIME_OUT);
292                 maxRetry++;
293             }
294         }
295         if (!work && (maxRetry >= MAX_RETRY)) {
296             ASSERT_TRUE(false) << "Wait for generating C2Work exceeded timeout";
297         }
298         if (signalEOS && (nFrames == 1))
299             flags |= C2FrameData::FLAG_END_OF_STREAM;
300         if (flushed) {
301             flags |= SYNC_FRAME;
302             flushed = false;
303         }
304         work->input.flags = (C2FrameData::flags_t)flags;
305         work->input.ordinal.timestamp = timestamp;
306         work->input.ordinal.frameIndex = frameID;
307         {
308             ULock l(queueLock);
309             flushedIndices.emplace_back(frameID);
310         }
311         char* data = (char*)malloc(bytesCount);
312         ASSERT_NE(data, nullptr);
313         eleStream.read(data, bytesCount);
314         ASSERT_EQ(eleStream.gcount(), bytesCount);
315         std::shared_ptr<C2LinearBlock> block;
316         ASSERT_EQ(C2_OK, linearPool->fetchLinearBlock(
317                              bytesCount, {C2MemoryUsage::CPU_READ,
318                                           C2MemoryUsage::CPU_WRITE},
319                              &block));
320         ASSERT_TRUE(block);
321         // Write View
322         C2WriteView view = block->map().get();
323         if (view.error() != C2_OK) {
324             fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error());
325             break;
326         }
327         ASSERT_EQ((size_t)bytesCount, view.capacity());
328         ASSERT_EQ(0u, view.offset());
329         ASSERT_EQ((size_t)bytesCount, view.size());
330 
331         memcpy(view.base(), data, bytesCount);
332         work->input.buffers.clear();
333         work->input.buffers.emplace_back(new LinearBuffer(block));
334         work->worklets.clear();
335         work->worklets.emplace_back(new C2Worklet);
336         free(data);
337 
338         std::list<std::unique_ptr<C2Work>> items;
339         items.push_back(std::move(work));
340 
341         // DO THE DECODING
342         ASSERT_EQ(component->queue(&items), C2_OK);
343         ALOGV("Frame #%d size = %d queued", frameID, bytesCount);
344         nFrames--;
345         timestamp += timestampIncr;
346         frameID++;
347         maxRetry = 0;
348     }
349 }
350 
TEST_F(Codec2AudioEncHidlTest,validateCompName)351 TEST_F(Codec2AudioEncHidlTest, validateCompName) {
352     if (mDisableTest) return;
353     ALOGV("Checks if the given component is a valid audio component");
354     validateComponent(mComponent, mCompName, mDisableTest);
355     ASSERT_EQ(mDisableTest, false);
356 }
357 
TEST_F(Codec2AudioEncHidlTest,EncodeTest)358 TEST_F(Codec2AudioEncHidlTest, EncodeTest) {
359     ALOGV("EncodeTest");
360     if (mDisableTest) return;
361     ASSERT_EQ(mComponent->start(), C2_OK);
362     char mURL[512];
363     strcpy(mURL, gEnv->getRes().c_str());
364     GetURLForComponent(mCompName, mURL);
365 
366     // Setting default configuration
367     int32_t nChannels = 2;
368     int32_t nSampleRate = 44100;
369     int32_t samplesPerFrame = 1024;
370     switch (mCompName) {
371         case aac:
372             nChannels = 2;
373             nSampleRate = 48000;
374             samplesPerFrame = 1024;
375             break;
376         case flac:
377             nChannels = 2;
378             nSampleRate = 48000;
379             samplesPerFrame = 1152;
380             break;
381         case opus:
382             nChannels = 2;
383             nSampleRate = 48000;
384             samplesPerFrame = 960;
385             break;
386         case amrnb:
387             nChannels = 1;
388             nSampleRate = 8000;
389             samplesPerFrame = 160;
390             break;
391         case amrwb:
392             nChannels = 1;
393             nSampleRate = 16000;
394             samplesPerFrame = 160;
395             break;
396         default:
397             ASSERT_TRUE(false);
398     }
399     setupConfigParam(mComponent, nChannels, nSampleRate);
400     std::ifstream eleStream;
401     uint32_t numFrames = 128;
402     eleStream.open(mURL, std::ifstream::binary);
403     ASSERT_EQ(eleStream.is_open(), true);
404     ALOGV("mURL : %s", mURL);
405     ASSERT_NO_FATAL_FAILURE(
406         encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
407                       mFlushedIndices, mLinearPool, eleStream, numFrames,
408                       samplesPerFrame, nChannels, nSampleRate));
409     // blocking call to ensures application to Wait till all the inputs are
410     // consumed
411     ASSERT_NO_FATAL_FAILURE(
412         waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
413     eleStream.close();
414     if (mFramesReceived != numFrames) {
415         ALOGE("Input buffer count and Output buffer count mismatch");
416         ALOGE("framesReceived : %d inputFrames : %u", mFramesReceived, numFrames);
417         ASSERT_TRUE(false);
418     }
419     if ((mCompName == flac || mCompName == opus || mCompName == aac)) {
420         if (!mCsd) {
421             ALOGE("CSD buffer missing");
422             ASSERT_TRUE(false);
423         }
424     }
425     ASSERT_EQ(mEos, true);
426     ASSERT_EQ(mComponent->stop(), C2_OK);
427 }
428 
TEST_F(Codec2AudioEncHidlTest,EOSTest)429 TEST_F(Codec2AudioEncHidlTest, EOSTest) {
430     description("Test empty input buffer with EOS flag");
431     if (mDisableTest) return;
432     ASSERT_EQ(mComponent->start(), C2_OK);
433 
434     typedef std::unique_lock<std::mutex> ULock;
435     std::unique_ptr<C2Work> work;
436     {
437         ULock l(mQueueLock);
438         if (!mWorkQueue.empty()) {
439             work.swap(mWorkQueue.front());
440             mWorkQueue.pop_front();
441         } else {
442             ALOGE("mWorkQueue Empty is not expected at the start of the test");
443             ASSERT_TRUE(false);
444         }
445     }
446     ASSERT_NE(work, nullptr);
447     work->input.flags = (C2FrameData::flags_t)C2FrameData::FLAG_END_OF_STREAM;
448     work->input.ordinal.timestamp = 0;
449     work->input.ordinal.frameIndex = 0;
450     work->input.buffers.clear();
451     work->worklets.clear();
452     work->worklets.emplace_back(new C2Worklet);
453 
454     std::list<std::unique_ptr<C2Work>> items;
455     items.push_back(std::move(work));
456     ASSERT_EQ(mComponent->queue(&items), C2_OK);
457     uint32_t queueSize;
458     {
459         ULock l(mQueueLock);
460         queueSize = mWorkQueue.size();
461         if (queueSize < MAX_INPUT_BUFFERS) {
462             mQueueCondition.wait_for(l, TIME_OUT);
463         }
464     }
465     ASSERT_EQ(mEos, true);
466     ASSERT_EQ(mComponent->stop(), C2_OK);
467 }
468 
TEST_F(Codec2AudioEncHidlTest,FlushTest)469 TEST_F(Codec2AudioEncHidlTest, FlushTest) {
470     description("Test Request for flush");
471     if (mDisableTest) return;
472     ASSERT_EQ(mComponent->start(), C2_OK);
473 
474     typedef std::unique_lock<std::mutex> ULock;
475     char mURL[512];
476     strcpy(mURL, gEnv->getRes().c_str());
477     GetURLForComponent(mCompName, mURL);
478 
479     // Setting default configuration
480     mFlushedIndices.clear();
481     int32_t nChannels = 2;
482     int32_t nSampleRate = 44100;
483     int32_t samplesPerFrame = 1024;
484     switch (mCompName) {
485         case aac:
486             nChannels = 2;
487             nSampleRate = 48000;
488             samplesPerFrame = 1024;
489             break;
490         case flac:
491             nChannels = 2;
492             nSampleRate = 48000;
493             samplesPerFrame = 1152;
494             break;
495         case opus:
496             nChannels = 2;
497             nSampleRate = 48000;
498             samplesPerFrame = 960;
499             break;
500         case amrnb:
501             nChannels = 1;
502             nSampleRate = 8000;
503             samplesPerFrame = 160;
504             break;
505         case amrwb:
506             nChannels = 1;
507             nSampleRate = 16000;
508             samplesPerFrame = 160;
509             break;
510         default:
511             ASSERT_TRUE(false);
512     }
513     setupConfigParam(mComponent, nChannels, nSampleRate);
514     std::ifstream eleStream;
515     uint32_t numFramesFlushed = 30;
516     uint32_t numFrames = 128;
517     eleStream.open(mURL, std::ifstream::binary);
518     ASSERT_EQ(eleStream.is_open(), true);
519     ALOGV("mURL : %s", mURL);
520     ASSERT_NO_FATAL_FAILURE(
521         encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
522                       mFlushedIndices, mLinearPool, eleStream, numFramesFlushed,
523                       samplesPerFrame, nChannels, nSampleRate));
524     std::list<std::unique_ptr<C2Work>> flushedWork;
525     c2_status_t err =
526         mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
527     ASSERT_EQ(err, C2_OK);
528     ASSERT_NO_FATAL_FAILURE(
529         waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
530             (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
531     uint64_t frameIndex;
532     {
533         //Update mFlushedIndices based on the index received from flush()
534         ULock l(mQueueLock);
535         for (std::unique_ptr<C2Work>& work : flushedWork) {
536             ASSERT_NE(work, nullptr);
537             frameIndex = work->input.ordinal.frameIndex.peeku();
538             std::list<uint64_t>::iterator frameIndexIt =
539                 std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
540                           frameIndex);
541             if (!mFlushedIndices.empty() &&
542                 (frameIndexIt != mFlushedIndices.end())) {
543                 mFlushedIndices.erase(frameIndexIt);
544                 work->input.buffers.clear();
545                 work->worklets.clear();
546                 mWorkQueue.push_back(std::move(work));
547             }
548         }
549     }
550     mFlushedIndices.clear();
551     ASSERT_NO_FATAL_FAILURE(
552         encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
553                       mFlushedIndices, mLinearPool, eleStream,
554                       numFrames - numFramesFlushed, samplesPerFrame,
555                       nChannels, nSampleRate, true));
556     eleStream.close();
557     err =
558         mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
559     ASSERT_EQ(err, C2_OK);
560     ASSERT_NO_FATAL_FAILURE(
561         waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
562             (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
563     {
564         //Update mFlushedIndices based on the index received from flush()
565         ULock l(mQueueLock);
566         for (std::unique_ptr<C2Work>& work : flushedWork) {
567             ASSERT_NE(work, nullptr);
568             frameIndex = work->input.ordinal.frameIndex.peeku();
569             std::list<uint64_t>::iterator frameIndexIt =
570                 std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
571                           frameIndex);
572             if (!mFlushedIndices.empty() &&
573                 (frameIndexIt != mFlushedIndices.end())) {
574                 mFlushedIndices.erase(frameIndexIt);
575                 work->input.buffers.clear();
576                 work->worklets.clear();
577                 mWorkQueue.push_back(std::move(work));
578             }
579         }
580     }
581     ASSERT_EQ(mFlushedIndices.empty(), true);
582     ASSERT_EQ(mComponent->stop(), C2_OK);
583 }
584 
585 }  // anonymous namespace
586 
main(int argc,char ** argv)587 int main(int argc, char** argv) {
588     gEnv = new ComponentTestEnvironment();
589     ::testing::AddGlobalTestEnvironment(gEnv);
590     ::testing::InitGoogleTest(&argc, argv);
591     gEnv->init(&argc, argv);
592     int status = gEnv->initFromOptions(argc, argv);
593     if (status == 0) {
594         int status = RUN_ALL_TESTS();
595         LOG(INFO) << "C2 Test result = " << status;
596     }
597     return status;
598 }
599