From f533b50925926a55b23550bea52fa044df144b39 Mon Sep 17 00:00:00 2001 From: Sneha Patil Date: Wed, 25 Oct 2023 12:39:55 +0530 Subject: [PATCH] DownmixProcess: Add tests to validate the downmix process. Added methods to set and validate parameters. Added test to verify multichannel to stereo conversion for STRIP type. Added test to verify multichannel to stereo conversion for FOLD type. Bug:314953788 Test: atest VtsHalDownmixTargetTest Change-Id: I31ee4df3eac6ae42e3e3cabebd1622e860581eab --- audio/aidl/vts/Android.bp | 3 + audio/aidl/vts/EffectHelper.h | 28 ++ audio/aidl/vts/VtsHalDownmixTargetTest.cpp | 440 ++++++++++++++++++--- 3 files changed, 414 insertions(+), 57 deletions(-) diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp index ad816c7056..191f928a53 100644 --- a/audio/aidl/vts/Android.bp +++ b/audio/aidl/vts/Android.bp @@ -86,6 +86,9 @@ cc_test { name: "VtsHalDownmixTargetTest", defaults: ["VtsHalAudioTargetTestDefaults"], srcs: ["VtsHalDownmixTargetTest.cpp"], + shared_libs: [ + "libaudioutils", + ], } cc_test { diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h index 2c8edf28fb..d813554221 100644 --- a/audio/aidl/vts/EffectHelper.h +++ b/audio/aidl/vts/EffectHelper.h @@ -283,4 +283,32 @@ class EffectHelper { } return functor(result); } + + static void processAndWriteToOutput(std::vector& inputBuffer, + std::vector& outputBuffer, + const std::shared_ptr& mEffect, + IEffect::OpenEffectReturn* mOpenEffectReturn) { + // Initialize AidlMessagequeues + auto statusMQ = std::make_unique(mOpenEffectReturn->statusMQ); + ASSERT_TRUE(statusMQ->isValid()); + auto inputMQ = std::make_unique(mOpenEffectReturn->inputDataMQ); + ASSERT_TRUE(inputMQ->isValid()); + auto outputMQ = std::make_unique(mOpenEffectReturn->outputDataMQ); + ASSERT_TRUE(outputMQ->isValid()); + + // Enabling the process + ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::START)); + ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::PROCESSING)); + + // Write from buffer to message queues and calling process + EXPECT_NO_FATAL_FAILURE(EffectHelper::writeToFmq(statusMQ, inputMQ, inputBuffer)); + + // Read the updated message queues into buffer + EXPECT_NO_FATAL_FAILURE(EffectHelper::readFromFmq(statusMQ, 1, outputMQ, + outputBuffer.size(), outputBuffer)); + + // Disable the process + ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::RESET)); + ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE)); + } }; diff --git a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp index c01a9a2d36..d7db567a0b 100644 --- a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp +++ b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp @@ -18,88 +18,114 @@ #define LOG_TAG "VtsHalDownmixTargetTest" #include +#include #include "EffectHelper.h" using namespace android; +using aidl::android::hardware::audio::common::getChannelCount; using aidl::android::hardware::audio::effect::Descriptor; using aidl::android::hardware::audio::effect::Downmix; using aidl::android::hardware::audio::effect::getEffectTypeUuidDownmix; using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; +using android::audio_utils::channels::ChannelMix; using android::hardware::audio::common::testing::detail::TestExecutionTracer; -/** - * Here we focus on specific parameter checking, general IEffect interfaces testing performed in - * VtsAudioEffectTargetTest. - */ -enum ParamName { PARAM_INSTANCE_NAME, PARAM_TYPE }; -using DownmixParamTestParam = - std::tuple, Descriptor>, Downmix::Type>; // Testing for enum values -const std::vector kTypeValues = {Downmix::Type::STRIP, Downmix::Type::FOLD}; +static const std::vector kTypeValues = {ndk::enum_range().begin(), + ndk::enum_range().end()}; -class DownmixParamTest : public ::testing::TestWithParam, - public EffectHelper { +// Testing for supported layouts from AudioChannelLayout.h +static const std::vector kLayoutValues = { + AudioChannelLayout::LAYOUT_STEREO, AudioChannelLayout::LAYOUT_2POINT1, + AudioChannelLayout::LAYOUT_TRI, AudioChannelLayout::LAYOUT_TRI_BACK, + AudioChannelLayout::LAYOUT_3POINT1, AudioChannelLayout::LAYOUT_2POINT0POINT2, + AudioChannelLayout::LAYOUT_2POINT1POINT2, AudioChannelLayout::LAYOUT_3POINT0POINT2, + AudioChannelLayout::LAYOUT_3POINT1POINT2, AudioChannelLayout::LAYOUT_QUAD, + AudioChannelLayout::LAYOUT_QUAD_SIDE, AudioChannelLayout::LAYOUT_SURROUND, + AudioChannelLayout::LAYOUT_PENTA, AudioChannelLayout::LAYOUT_5POINT1, + AudioChannelLayout::LAYOUT_5POINT1_SIDE, AudioChannelLayout::LAYOUT_5POINT1POINT2, + AudioChannelLayout::LAYOUT_5POINT1POINT4, AudioChannelLayout::LAYOUT_6POINT1, + AudioChannelLayout::LAYOUT_7POINT1, AudioChannelLayout::LAYOUT_7POINT1POINT2, + AudioChannelLayout::LAYOUT_7POINT1POINT4, AudioChannelLayout::LAYOUT_9POINT1POINT4, + AudioChannelLayout::LAYOUT_9POINT1POINT6, AudioChannelLayout::LAYOUT_13POINT_360RA, + AudioChannelLayout::LAYOUT_22POINT2}; + +static const std::vector kChannels = { + AudioChannelLayout::CHANNEL_FRONT_LEFT, + AudioChannelLayout::CHANNEL_FRONT_RIGHT, + AudioChannelLayout::CHANNEL_FRONT_CENTER, + AudioChannelLayout::CHANNEL_LOW_FREQUENCY, + AudioChannelLayout::CHANNEL_BACK_LEFT, + AudioChannelLayout::CHANNEL_BACK_RIGHT, + AudioChannelLayout::CHANNEL_BACK_CENTER, + AudioChannelLayout::CHANNEL_SIDE_LEFT, + AudioChannelLayout::CHANNEL_SIDE_RIGHT, + AudioChannelLayout::CHANNEL_FRONT_LEFT_OF_CENTER, + AudioChannelLayout::CHANNEL_FRONT_RIGHT_OF_CENTER, + AudioChannelLayout::CHANNEL_TOP_CENTER, + AudioChannelLayout::CHANNEL_TOP_FRONT_LEFT, + AudioChannelLayout::CHANNEL_TOP_FRONT_CENTER, + AudioChannelLayout::CHANNEL_TOP_FRONT_RIGHT, + AudioChannelLayout::CHANNEL_TOP_BACK_LEFT, + AudioChannelLayout::CHANNEL_TOP_BACK_CENTER, + AudioChannelLayout::CHANNEL_TOP_BACK_RIGHT, + AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT, + AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT, + AudioChannelLayout::CHANNEL_BOTTOM_FRONT_LEFT, + AudioChannelLayout::CHANNEL_BOTTOM_FRONT_CENTER, + AudioChannelLayout::CHANNEL_BOTTOM_FRONT_RIGHT, + AudioChannelLayout::CHANNEL_LOW_FREQUENCY_2, + AudioChannelLayout::CHANNEL_FRONT_WIDE_LEFT, + AudioChannelLayout::CHANNEL_FRONT_WIDE_RIGHT, +}; + +class DownmixEffectHelper : public EffectHelper { public: - DownmixParamTest() : mParamType(std::get(GetParam())) { - std::tie(mFactory, mDescriptor) = std::get(GetParam()); - } - - void SetUp() override { + void SetUpDownmix(int32_t inputBufferLayout = AudioChannelLayout::LAYOUT_STEREO) { ASSERT_NE(nullptr, mFactory); ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor)); + AudioChannelLayout inputChannelLayout = + AudioChannelLayout::make(inputBufferLayout); + Parameter::Specific specific = getDefaultParamSpecific(); Parameter::Common common = EffectHelper::createParamCommon( 0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */, - kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */); - IEffect::OpenEffectReturn ret; - ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &ret, EX_NONE)); + kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */, + inputChannelLayout); + ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &mOpenEffectReturn, EX_NONE)); ASSERT_NE(nullptr, mEffect); } - void TearDown() override { + void TearDownDownmix() { ASSERT_NO_FATAL_FAILURE(close(mEffect)); ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect)); + mOpenEffectReturn = IEffect::OpenEffectReturn{}; } - static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100; - std::shared_ptr mFactory; - std::shared_ptr mEffect; - Descriptor mDescriptor; - Downmix::Type mParamType = Downmix::Type::STRIP; - - void SetAndGetDownmixParameters() { - for (auto& it : mTags) { - auto& tag = it.first; - auto& dm = it.second; - - // set parameter - Parameter expectParam; - Parameter::Specific specific; - specific.set(dm); - expectParam.set(specific); - // All values are valid, set parameter should succeed - EXPECT_STATUS(EX_NONE, mEffect->setParameter(expectParam)) << expectParam.toString(); - - // get parameter - Parameter getParam; - Parameter::Id id; - Downmix::Id dmId; - dmId.set(tag); - id.set(dmId); - EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam)); - - EXPECT_EQ(expectParam, getParam); - } + Parameter createDownmixParam(Downmix::Type type) { + return Parameter::make( + Parameter::Specific::make( + Downmix::make(type))); + } + void setParameters(Downmix::Type type) { + // set parameter + auto param = createDownmixParam(type); + EXPECT_STATUS(EX_NONE, mEffect->setParameter(param)) << param.toString(); } - void addTypeParam(Downmix::Type type) { - Downmix dm; - dm.set(type); - mTags.push_back({Downmix::type, dm}); + void validateParameters(Downmix::Type type) { + auto leId = Downmix::Id::make(Downmix::Tag(Downmix::type)); + auto id = Parameter::Id::make(leId); + // get parameter + Parameter getParam; + EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam)); + auto expectedParam = createDownmixParam(type); + EXPECT_EQ(expectedParam, getParam) << "\nexpectedParam:" << expectedParam.toString() + << "\ngetParam:" << getParam.toString(); } Parameter::Specific getDefaultParamSpecific() { @@ -108,14 +134,281 @@ class DownmixParamTest : public ::testing::TestWithParam, return specific; } - private: - std::vector> mTags; - void CleanUp() { mTags.clear(); } + void setDataTestParams(int32_t layoutType) { + mInputBuffer.resize(kBufferSize); + mOutputBuffer.resize(kBufferSize); + + // Get the number of channels used + mInputChannelCount = getChannelCount( + AudioChannelLayout::make(layoutType)); + + // In case of downmix, output is always configured to stereo layout. + mOutputBufferSize = (mInputBuffer.size() / mInputChannelCount) * kOutputChannelCount; + } + + // Generate mInputBuffer values between -kMaxDownmixSample to kMaxDownmixSample + void generateInputBuffer(size_t position, bool isStrip) { + size_t increment; + if (isStrip) + // Fill input at all the channels + increment = 1; + else + // Fill input at only one channel + increment = mInputChannelCount; + + for (size_t i = position; i < mInputBuffer.size(); i += increment) { + mInputBuffer[i] = + ((static_cast(std::rand()) / RAND_MAX) * 2 - 1) * kMaxDownmixSample; + } + } + + bool isLayoutValid(int32_t inputLayout) { + if (inputLayout & kMaxChannelMask) { + return false; + } + return true; + } + + static constexpr long kInputFrameCount = 100, kOutputFrameCount = 100; + std::shared_ptr mFactory; + Descriptor mDescriptor; + std::shared_ptr mEffect; + IEffect::OpenEffectReturn mOpenEffectReturn; + + std::vector mInputBuffer; + std::vector mOutputBuffer; + size_t mInputChannelCount; + size_t mOutputBufferSize; + static constexpr size_t kBufferSize = 128; + static constexpr float kMaxDownmixSample = 1; + static constexpr int kOutputChannelCount = 2; + // Mask for layouts greater than MAX_INPUT_CHANNELS_SUPPORTED + static constexpr int32_t kMaxChannelMask = + ~((1 << ChannelMix::MAX_INPUT_CHANNELS_SUPPORTED) - 1); +}; + +/** + * Here we focus on specific parameter checking, general IEffect interfaces testing performed in + * VtsAudioEffectTargetTest. + */ +enum ParamName { PARAM_INSTANCE_NAME, PARAM_TYPE }; + +using DownmixParamTestParam = + std::tuple, Descriptor>, Downmix::Type>; + +class DownmixParamTest : public ::testing::TestWithParam, + public DownmixEffectHelper { + public: + DownmixParamTest() : mParamType(std::get(GetParam())) { + std::tie(mFactory, mDescriptor) = std::get(GetParam()); + } + + void SetUp() override { SetUpDownmix(); } + + void TearDown() override { TearDownDownmix(); } + + const Downmix::Type mParamType; }; TEST_P(DownmixParamTest, SetAndGetType) { - EXPECT_NO_FATAL_FAILURE(addTypeParam(mParamType)); - SetAndGetDownmixParameters(); + ASSERT_NO_FATAL_FAILURE(setParameters(mParamType)); + ASSERT_NO_FATAL_FAILURE(validateParameters(mParamType)); +} + +enum FoldParamName { FOLD_INSTANCE_NAME, FOLD_INPUT_LAYOUT, FOLD_TEST_CHANNEL }; + +using DownmixDataTestParamFold = + std::tuple, Descriptor>, int32_t>; + +class DownmixFoldDataTest : public ::testing::TestWithParam, + public DownmixEffectHelper { + public: + DownmixFoldDataTest() : mInputChannelLayout(std::get(GetParam())) { + std::tie(mFactory, mDescriptor) = std::get(GetParam()); + } + + void SetUp() override { + SetUpDownmix(mInputChannelLayout); + if (!isLayoutValid(mInputChannelLayout)) { + GTEST_SKIP() << "Layout not supported \n"; + } + setDataTestParams(mInputChannelLayout); + } + + void TearDown() override { TearDownDownmix(); } + + void checkAtLeft(int32_t position) { + for (size_t i = 0, j = position; i < mOutputBufferSize; + i += kOutputChannelCount, j += mInputChannelCount) { + // Validate Left channel has audio + if (mInputBuffer[j] != 0) { + ASSERT_NE(mOutputBuffer[i], 0); + } else { + // No change in output when input is 0 + ASSERT_EQ(mOutputBuffer[i], mInputBuffer[j]); + } + // Validate Right channel has no audio + ASSERT_EQ(mOutputBuffer[i + 1], 0); + } + } + + void checkAtRight(int32_t position) { + for (size_t i = 0, j = position; i < mOutputBufferSize; + i += kOutputChannelCount, j += mInputChannelCount) { + // Validate Left channel has no audio + ASSERT_EQ(mOutputBuffer[i], 0); + // Validate Right channel has audio + if (mInputBuffer[j] != 0) { + ASSERT_NE(mOutputBuffer[i + 1], 0); + } else { + // No change in output when input is 0 + ASSERT_EQ(mOutputBuffer[i + 1], mInputBuffer[j]); + } + } + } + + void checkAtCenter(size_t position) { + for (size_t i = 0, j = position; i < mOutputBufferSize; + i += kOutputChannelCount, j += mInputChannelCount) { + // Validate both channels have audio + if (mInputBuffer[j] != 0) { + ASSERT_NE(mOutputBuffer[i], 0); + ASSERT_NE(mOutputBuffer[i + 1], 0); + + } else { + // No change in output when input is 0 + ASSERT_EQ(mOutputBuffer[i], mInputBuffer[j]); + ASSERT_EQ(mOutputBuffer[i + 1], mInputBuffer[j]); + } + } + } + + void validateOutput(int32_t channel, size_t position) { + switch (channel) { + case AudioChannelLayout::CHANNEL_FRONT_LEFT: + case AudioChannelLayout::CHANNEL_BACK_LEFT: + case AudioChannelLayout::CHANNEL_SIDE_LEFT: + case AudioChannelLayout::CHANNEL_TOP_FRONT_LEFT: + case AudioChannelLayout::CHANNEL_BOTTOM_FRONT_LEFT: + case AudioChannelLayout::CHANNEL_TOP_BACK_LEFT: + case AudioChannelLayout::CHANNEL_FRONT_WIDE_LEFT: + case AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT: + checkAtLeft(position); + break; + + case AudioChannelLayout::CHANNEL_FRONT_RIGHT: + case AudioChannelLayout::CHANNEL_BACK_RIGHT: + case AudioChannelLayout::CHANNEL_SIDE_RIGHT: + case AudioChannelLayout::CHANNEL_TOP_FRONT_RIGHT: + case AudioChannelLayout::CHANNEL_BOTTOM_FRONT_RIGHT: + case AudioChannelLayout::CHANNEL_TOP_BACK_RIGHT: + case AudioChannelLayout::CHANNEL_FRONT_WIDE_RIGHT: + case AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT: + case AudioChannelLayout::CHANNEL_LOW_FREQUENCY_2: + checkAtRight(position); + break; + + case AudioChannelLayout::CHANNEL_FRONT_CENTER: + case AudioChannelLayout::CHANNEL_BACK_CENTER: + case AudioChannelLayout::CHANNEL_TOP_FRONT_CENTER: + case AudioChannelLayout::CHANNEL_BOTTOM_FRONT_CENTER: + case AudioChannelLayout::CHANNEL_FRONT_LEFT_OF_CENTER: + case AudioChannelLayout::CHANNEL_FRONT_RIGHT_OF_CENTER: + case AudioChannelLayout::CHANNEL_TOP_CENTER: + case AudioChannelLayout::CHANNEL_TOP_BACK_CENTER: + checkAtCenter(position); + break; + + case AudioChannelLayout::CHANNEL_LOW_FREQUENCY: + // If CHANNEL_LOW_FREQUENCY_2 is supported + if (mInputChannelLayout & AudioChannelLayout::CHANNEL_LOW_FREQUENCY_2) { + // Validate that only Left channel has audio + checkAtLeft(position); + } else { + // Validate that both channels have audio + checkAtCenter(position); + } + break; + } + } + + std::set getInputChannelLayouts() { + std::set supportedChannels; + for (int32_t channel : kChannels) { + if ((mInputChannelLayout & channel) == channel) { + supportedChannels.insert(channel); + } + } + return supportedChannels; + } + + int32_t mInputChannelLayout; +}; + +TEST_P(DownmixFoldDataTest, DownmixProcessData) { + // Set FOLD type parameter + ASSERT_NO_FATAL_FAILURE(setParameters(Downmix::Type::FOLD)); + + // Get all the channels from input layout + std::set supportedChannels = getInputChannelLayouts(); + + for (int32_t channel : supportedChannels) { + size_t position = std::distance(supportedChannels.begin(), supportedChannels.find(channel)); + generateInputBuffer(position, false /*isStripe*/); + ASSERT_NO_FATAL_FAILURE( + processAndWriteToOutput(mInputBuffer, mOutputBuffer, mEffect, &mOpenEffectReturn)); + validateOutput(channel, position); + std::fill(mInputBuffer.begin(), mInputBuffer.end(), 0); + } +} + +enum StripParamName { STRIP_INSTANCE_NAME, STRIP_INPUT_LAYOUT }; + +using DownmixStripDataTestParam = + std::tuple, Descriptor>, int32_t>; + +class DownmixStripDataTest : public ::testing::TestWithParam, + public DownmixEffectHelper { + public: + DownmixStripDataTest() : mInputChannelLayout(std::get(GetParam())) { + std::tie(mFactory, mDescriptor) = std::get(GetParam()); + } + + void SetUp() override { + SetUpDownmix(mInputChannelLayout); + if (!isLayoutValid(mInputChannelLayout)) { + GTEST_SKIP() << "Layout not supported \n"; + } + setDataTestParams(mInputChannelLayout); + } + + void TearDown() override { TearDownDownmix(); } + + void validateOutput() { + ASSERT_EQ(kBufferSize, mInputBuffer.size()); + ASSERT_GE(kBufferSize, mOutputBufferSize); + for (size_t i = 0, j = 0; i < kBufferSize && j < mOutputBufferSize; + i += mInputChannelCount, j += kOutputChannelCount) { + ASSERT_EQ(mOutputBuffer[j], mInputBuffer[i]); + ASSERT_EQ(mOutputBuffer[j + 1], mInputBuffer[i + 1]); + } + for (size_t i = mOutputBufferSize; i < kBufferSize; i++) { + ASSERT_EQ(mOutputBuffer[i], mInputBuffer[i]); + } + } + + int32_t mInputChannelLayout; +}; + +TEST_P(DownmixStripDataTest, DownmixProcessData) { + // Set STRIP type parameter + ASSERT_NO_FATAL_FAILURE(setParameters(Downmix::Type::STRIP)); + + // Generate input buffer, call process and compare outputs + generateInputBuffer(0 /*position*/, true /*isStripe*/); + ASSERT_NO_FATAL_FAILURE( + processAndWriteToOutput(mInputBuffer, mOutputBuffer, mEffect, &mOpenEffectReturn)); + validateOutput(); } INSTANTIATE_TEST_SUITE_P( @@ -134,6 +427,39 @@ INSTANTIATE_TEST_SUITE_P( GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DownmixParamTest); +INSTANTIATE_TEST_SUITE_P( + DownmixTest, DownmixFoldDataTest, + ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, getEffectTypeUuidDownmix())), + testing::ValuesIn(kLayoutValues)), + [](const testing::TestParamInfo& info) { + auto descriptor = std::get(info.param).second; + std::string layout = std::to_string(std::get(info.param)); + std::string name = getPrefix(descriptor) + "_fold" + "_layout" + layout; + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DownmixFoldDataTest); + +INSTANTIATE_TEST_SUITE_P( + DownmixTest, DownmixStripDataTest, + ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, getEffectTypeUuidDownmix())), + testing::ValuesIn(kLayoutValues)), + [](const testing::TestParamInfo& info) { + auto descriptor = std::get(info.param).second; + std::string layout = + std::to_string(static_cast(std::get(info.param))); + std::string name = getPrefix(descriptor) + "_strip" + "_layout" + layout; + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DownmixStripDataTest); + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());