Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d921f613 authored by Tomasz Wasilczyk's avatar Tomasz Wasilczyk
Browse files

Implement VTS tests for new radio interfaces.

Test: VTS
Bug: 65862441
Change-Id: I57bd413f011433fbbd432b886cbd5ad60e05a57e
parent 45491be2
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
# Automotive team
egranata@google.com
twasilczyk@google.com

# VTS team
ryanjcampbell@google.com
yim@google.com
+29 −0
Original line number Diff line number Diff line
//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

cc_test {
    name: "VtsHalBroadcastradioV1_2TargetTest",
    defaults: ["VtsHalTargetTestDefaults"],
    srcs: ["VtsHalBroadcastradioV1_2TargetTest.cpp"],
    static_libs: [
        "android.hardware.broadcastradio@1.0",
        "android.hardware.broadcastradio@1.1",
        "android.hardware.broadcastradio@1.2",
        "android.hardware.broadcastradio@common-utils-lib",
        "android.hardware.broadcastradio@vts-utils-lib",
        "libgmock",
    ],
}
+312 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "broadcastradio.vts"

#include <VtsHalHidlTargetTestBase.h>
#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
#include <android/hardware/broadcastradio/1.2/IBroadcastRadioFactory.h>
#include <android/hardware/broadcastradio/1.2/ITuner.h>
#include <android/hardware/broadcastradio/1.2/ITunerCallback.h>
#include <android/hardware/broadcastradio/1.2/types.h>
#include <android-base/logging.h>
#include <broadcastradio-utils/Utils.h>
#include <broadcastradio-vts-utils/call-barrier.h>
#include <broadcastradio-vts-utils/mock-timeout.h>
#include <cutils/native_handle.h>
#include <cutils/properties.h>
#include <gmock/gmock.h>
#include <hidl/HidlTransportSupport.h>
#include <utils/threads.h>

#include <chrono>

namespace android {
namespace hardware {
namespace broadcastradio {
namespace V1_2 {
namespace vts {

using namespace std::chrono_literals;

using testing::_;
using testing::AnyNumber;
using testing::ByMove;
using testing::DoAll;
using testing::Invoke;
using testing::SaveArg;

using broadcastradio::vts::CallBarrier;
using V1_0::BandConfig;
using V1_0::Class;
using V1_0::MetaData;
using V1_0::MetadataKey;
using V1_0::MetadataType;
using V1_1::IBroadcastRadio;
using V1_1::ProgramInfo;
using V1_1::ProgramListResult;
using V1_1::ProgramSelector;
using V1_1::Properties;

using std::chrono::steady_clock;
using std::this_thread::sleep_for;

static constexpr auto kConfigTimeout = 10s;
static constexpr auto kConnectModuleTimeout = 1s;

static void printSkipped(std::string msg) {
    std::cout << "[  SKIPPED ] " << msg << std::endl;
}

struct TunerCallbackMock : public ITunerCallback {
    TunerCallbackMock() { EXPECT_CALL(*this, hardwareFailure()).Times(0); }

    MOCK_METHOD0(hardwareFailure, Return<void>());
    MOCK_TIMEOUT_METHOD2(configChange, Return<void>(Result, const BandConfig&));
    MOCK_METHOD2(tuneComplete, Return<void>(Result, const V1_0::ProgramInfo&));
    MOCK_TIMEOUT_METHOD2(tuneComplete_1_1, Return<void>(Result, const ProgramSelector&));
    MOCK_METHOD1(afSwitch, Return<void>(const V1_0::ProgramInfo&));
    MOCK_METHOD1(antennaStateChange, Return<void>(bool connected));
    MOCK_METHOD1(trafficAnnouncement, Return<void>(bool active));
    MOCK_METHOD1(emergencyAnnouncement, Return<void>(bool active));
    MOCK_METHOD3(newMetadata, Return<void>(uint32_t ch, uint32_t subCh, const hidl_vec<MetaData>&));
    MOCK_METHOD1(backgroundScanAvailable, Return<void>(bool));
    MOCK_TIMEOUT_METHOD1(backgroundScanComplete, Return<void>(ProgramListResult));
    MOCK_METHOD0(programListChanged, Return<void>());
    MOCK_TIMEOUT_METHOD1(currentProgramInfoChanged, Return<void>(const ProgramInfo&));
    MOCK_METHOD1(parametersUpdated, Return<void>(const hidl_vec<VendorKeyValue>& parameters));
};

class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase,
                              public ::testing::WithParamInterface<Class> {
   protected:
    virtual void SetUp() override;
    virtual void TearDown() override;

    bool openTuner();

    Class radioClass;
    bool skipped = false;

    sp<IBroadcastRadio> mRadioModule;
    sp<ITuner> mTuner;
    sp<TunerCallbackMock> mCallback = new TunerCallbackMock();

   private:
    const BandConfig& getBand(unsigned idx);

    hidl_vec<BandConfig> mBands;
};

/**
 * Clears strong pointer and waits until the object gets destroyed.
 *
 * @param ptr The pointer to get cleared.
 * @param timeout Time to wait for other references.
 */
template <typename T>
static void clearAndWait(sp<T>& ptr, std::chrono::milliseconds timeout) {
    wp<T> wptr = ptr;
    ptr.clear();
    auto limit = steady_clock::now() + timeout;
    while (wptr.promote() != nullptr) {
        constexpr auto step = 10ms;
        if (steady_clock::now() + step > limit) {
            FAIL() << "Pointer was not released within timeout";
            break;
        }
        sleep_for(step);
    }
}

void BroadcastRadioHalTest::SetUp() {
    radioClass = GetParam();

    // lookup HIDL service
    auto factory = getService<IBroadcastRadioFactory>();
    ASSERT_NE(nullptr, factory.get());

    // connect radio module
    Result connectResult;
    CallBarrier onConnect;
    factory->connectModule(radioClass, [&](Result ret, const sp<V1_0::IBroadcastRadio>& radio) {
        connectResult = ret;
        if (ret == Result::OK) mRadioModule = IBroadcastRadio::castFrom(radio);
        onConnect.call();
    });
    ASSERT_TRUE(onConnect.waitForCall(kConnectModuleTimeout));

    if (connectResult == Result::INVALID_ARGUMENTS) {
        printSkipped("This device class is not supported.");
        skipped = true;
        return;
    }
    ASSERT_EQ(connectResult, Result::OK);
    ASSERT_NE(nullptr, mRadioModule.get());

    // get module properties
    Properties prop11;
    auto& prop10 = prop11.base;
    auto propResult =
        mRadioModule->getProperties_1_1([&](const Properties& properties) { prop11 = properties; });

    ASSERT_TRUE(propResult.isOk());
    EXPECT_EQ(radioClass, prop10.classId);
    EXPECT_GT(prop10.numTuners, 0u);
    EXPECT_GT(prop11.supportedProgramTypes.size(), 0u);
    EXPECT_GT(prop11.supportedIdentifierTypes.size(), 0u);
    if (radioClass == Class::AM_FM) {
        EXPECT_GT(prop10.bands.size(), 0u);
    }
    mBands = prop10.bands;
}

void BroadcastRadioHalTest::TearDown() {
    mTuner.clear();
    mRadioModule.clear();
    clearAndWait(mCallback, 1s);
}

bool BroadcastRadioHalTest::openTuner() {
    EXPECT_EQ(nullptr, mTuner.get());

    if (radioClass == Class::AM_FM) {
        EXPECT_TIMEOUT_CALL(*mCallback, configChange, Result::OK, _);
    }

    Result halResult = Result::NOT_INITIALIZED;
    auto openCb = [&](Result result, const sp<V1_0::ITuner>& tuner) {
        halResult = result;
        if (result != Result::OK) return;
        mTuner = ITuner::castFrom(tuner);
    };
    auto hidlResult = mRadioModule->openTuner(getBand(0), true, mCallback, openCb);

    EXPECT_TRUE(hidlResult.isOk());
    EXPECT_EQ(Result::OK, halResult);
    EXPECT_NE(nullptr, mTuner.get());
    if (radioClass == Class::AM_FM && mTuner != nullptr) {
        EXPECT_TIMEOUT_CALL_WAIT(*mCallback, configChange, kConfigTimeout);

        BandConfig halConfig;
        Result halResult = Result::NOT_INITIALIZED;
        mTuner->getConfiguration([&](Result result, const BandConfig& config) {
            halResult = result;
            halConfig = config;
        });
        EXPECT_EQ(Result::OK, halResult);
        EXPECT_TRUE(halConfig.antennaConnected);
    }

    EXPECT_NE(nullptr, mTuner.get());
    return nullptr != mTuner.get();
}

const BandConfig& BroadcastRadioHalTest::getBand(unsigned idx) {
    static const BandConfig dummyBandConfig = {};

    if (radioClass != Class::AM_FM) {
        ALOGD("Not AM/FM radio, returning dummy band config");
        return dummyBandConfig;
    }

    EXPECT_GT(mBands.size(), idx);
    if (mBands.size() <= idx) {
        ALOGD("Band index out of bound, returning dummy band config");
        return dummyBandConfig;
    }

    auto& band = mBands[idx];
    ALOGD("Returning %s band", toString(band.type).c_str());
    return band;
}

/**
 * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
 *
 * Verifies that:
 *  - callback is called for empty parameters set.
 */
TEST_P(BroadcastRadioHalTest, NoParameters) {
    if (skipped) return;

    ASSERT_TRUE(openTuner());

    hidl_vec<VendorKeyValue> halResults = {};
    bool wasCalled = false;
    auto cb = [&](hidl_vec<VendorKeyValue> results) {
        wasCalled = true;
        halResults = results;
    };

    auto hidlResult = mTuner->setParameters({}, cb);
    ASSERT_TRUE(hidlResult.isOk());
    ASSERT_TRUE(wasCalled);
    ASSERT_EQ(0u, halResults.size());

    wasCalled = false;
    hidlResult = mTuner->getParameters({}, cb);
    ASSERT_TRUE(hidlResult.isOk());
    ASSERT_TRUE(wasCalled);
    ASSERT_EQ(0u, halResults.size());
}

/**
 * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
 *
 * Verifies that:
 *  - unknown parameters are ignored;
 *  - callback is called also for empty results set.
 */
TEST_P(BroadcastRadioHalTest, UnknownParameters) {
    if (skipped) return;

    ASSERT_TRUE(openTuner());

    hidl_vec<VendorKeyValue> halResults = {};
    bool wasCalled = false;
    auto cb = [&](hidl_vec<VendorKeyValue> results) {
        wasCalled = true;
        halResults = results;
    };

    auto hidlResult = mTuner->setParameters({{"com.google.unknown", "dummy"}}, cb);
    ASSERT_TRUE(hidlResult.isOk());
    ASSERT_TRUE(wasCalled);
    ASSERT_EQ(0u, halResults.size());

    wasCalled = false;
    hidlResult = mTuner->getParameters({{"com.google.unknown*", "dummy"}}, cb);
    ASSERT_TRUE(hidlResult.isOk());
    ASSERT_TRUE(wasCalled);
    ASSERT_EQ(0u, halResults.size());
}

INSTANTIATE_TEST_CASE_P(BroadcastRadioHalTestCases, BroadcastRadioHalTest,
                        ::testing::Values(Class::AM_FM, Class::SAT, Class::DT));

}  // namespace vts
}  // namespace V1_2
}  // namespace broadcastradio
}  // namespace hardware
}  // namespace android

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  int status = RUN_ALL_TESTS();
  ALOGI("Test result = %d", status);
  return status;
}
+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ subdirs = [
    "1.1/vts/functional",
    "1.2",
    "1.2/default",
    "1.2/vts/functional",
    "common/tests",
    "common/utils",
    "common/vts/utils",