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

Commit 6f6509f9 authored by Tomasz Wasilczyk's avatar Tomasz Wasilczyk Committed by Android (Google) Code Review
Browse files

Merge "Implement broadcast radio HAL 2.0 VTS tests."

parents b464e479 31e8632d
Loading
Loading
Loading
Loading
+2 −23
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <broadcastradio-utils-1x/Utils.h>
#include <broadcastradio-vts-utils/call-barrier.h>
#include <broadcastradio-vts-utils/mock-timeout.h>
#include <broadcastradio-vts-utils/pointer-utils.h>
#include <cutils/native_handle.h>
#include <cutils/properties.h>
#include <gmock/gmock.h>
@@ -56,8 +57,7 @@ using V1_0::MetaData;
using V1_0::MetadataKey;
using V1_0::MetadataType;

using std::chrono::steady_clock;
using std::this_thread::sleep_for;
using broadcastradio::vts::clearAndWait;

static constexpr auto kConfigTimeout = 10s;
static constexpr auto kConnectModuleTimeout = 1s;
@@ -115,27 +115,6 @@ class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase,
    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();

+3 −24
Original line number Diff line number Diff line
@@ -17,14 +17,15 @@
#define LOG_TAG "broadcastradio.vts"

#include <VtsHalHidlTargetTestBase.h>
#include <android-base/logging.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-vts-utils/call-barrier.h>
#include <broadcastradio-vts-utils/mock-timeout.h>
#include <broadcastradio-vts-utils/pointer-utils.h>
#include <cutils/native_handle.h>
#include <cutils/properties.h>
#include <gmock/gmock.h>
@@ -60,8 +61,7 @@ using V1_1::ProgramListResult;
using V1_1::ProgramSelector;
using V1_1::Properties;

using std::chrono::steady_clock;
using std::this_thread::sleep_for;
using broadcastradio::vts::clearAndWait;

static constexpr auto kConfigTimeout = 10s;
static constexpr auto kConnectModuleTimeout = 1s;
@@ -110,27 +110,6 @@ class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase,
    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();

+7 −0
Original line number Diff line number Diff line
# Automotive team
egranata@google.com
twasilczyk@google.com

# VTS team
yuexima@google.com
yim@google.com
+28 −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: "VtsHalBroadcastradioV2_0TargetTest",
    defaults: ["VtsHalTargetTestDefaults"],
    srcs: ["VtsHalBroadcastradioV2_0TargetTest.cpp"],
    static_libs: [
        "android.hardware.broadcastradio@2.0",
        "android.hardware.broadcastradio@common-utils-2x-lib",
        "android.hardware.broadcastradio@vts-utils-lib",
        "android.hardware.broadcastradio@vts-utils-lib",
        "libgmock",
    ],
}
+392 −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 "BcRadio.vts"

#include <VtsHalHidlTargetTestBase.h>
#include <android-base/logging.h>
#include <android/hardware/broadcastradio/2.0/IBroadcastRadio.h>
#include <android/hardware/broadcastradio/2.0/ITunerCallback.h>
#include <android/hardware/broadcastradio/2.0/ITunerSession.h>
#include <android/hardware/broadcastradio/2.0/types.h>
#include <broadcastradio-utils-2x/Utils.h>
#include <broadcastradio-vts-utils/call-barrier.h>
#include <broadcastradio-vts-utils/mock-timeout.h>
#include <broadcastradio-vts-utils/pointer-utils.h>
#include <gmock/gmock.h>

#include <chrono>

namespace android {
namespace hardware {
namespace broadcastradio {
namespace V2_0 {
namespace vts {

using namespace std::chrono_literals;

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

using broadcastradio::vts::CallBarrier;
using broadcastradio::vts::clearAndWait;
using utils::make_identifier;
using utils::make_selector_amfm;

namespace timeout {

static constexpr auto tune = 30s;

}  // namespace timeout

struct TunerCallbackMock : public ITunerCallback {
    TunerCallbackMock() {
        // we expect the antenna is connected through the whole test
        EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
    }

    MOCK_METHOD2(onTuneFailed, Return<void>(Result, const ProgramSelector&));
    MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChanged, Return<void>(const ProgramInfo&));
    MOCK_METHOD1(onAntennaStateChange, Return<void>(bool connected));
    MOCK_METHOD1(onParametersUpdated, Return<void>(const hidl_vec<VendorKeyValue>& parameters));
};

class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase {
   protected:
    virtual void SetUp() override;
    virtual void TearDown() override;

    bool openSession();

    sp<IBroadcastRadio> mModule;
    Properties mProperties;
    sp<ITunerSession> mSession;
    sp<TunerCallbackMock> mCallback = new TunerCallbackMock();
};

void BroadcastRadioHalTest::SetUp() {
    EXPECT_EQ(nullptr, mModule.get()) << "Module is already open";

    // lookup HIDL service (radio module)
    mModule = getService<IBroadcastRadio>();
    ASSERT_NE(nullptr, mModule.get()) << "Couldn't find broadcast radio HAL implementation";

    // get module properties
    auto propResult = mModule->getProperties([&](const Properties& p) { mProperties = p; });
    ASSERT_TRUE(propResult.isOk());

    EXPECT_FALSE(mProperties.maker.empty());
    EXPECT_FALSE(mProperties.product.empty());
    EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
}

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

bool BroadcastRadioHalTest::openSession() {
    EXPECT_EQ(nullptr, mSession.get()) << "Session is already open";

    Result halResult = Result::UNKNOWN_ERROR;
    auto openCb = [&](Result result, const sp<ITunerSession>& session) {
        halResult = result;
        if (result != Result::OK) return;
        mSession = session;
    };
    auto hidlResult = mModule->openSession(mCallback, openCb);

    EXPECT_TRUE(hidlResult.isOk());
    EXPECT_EQ(Result::OK, halResult);
    EXPECT_NE(nullptr, mSession.get());

    return nullptr != mSession.get();
}

/**
 * Test session opening.
 *
 * Verifies that:
 *  - the method succeeds on a first and subsequent calls;
 *  - the method succeeds when called for the second time without
 *    closing previous session.
 */
TEST_F(BroadcastRadioHalTest, OpenSession) {
    // simply open session for the first time
    ASSERT_TRUE(openSession());

    // drop (without explicit close) and re-open the session
    mSession.clear();
    ASSERT_TRUE(openSession());

    // open the second session (the first one should be forcibly closed)
    auto secondSession = mSession;
    mSession.clear();
    ASSERT_TRUE(openSession());
}

/**
 * Test tuning with FM selector.
 *
 * Verifies that:
 *  - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
 *  - if it is supported, the method succeeds;
 *  - after a successful tune call, onCurrentProgramInfoChanged callback is
 *    invoked carrying a proper selector;
 *  - program changes exactly to what was requested.
 */
TEST_F(BroadcastRadioHalTest, FmTune) {
    ASSERT_TRUE(openSession());

    uint64_t freq = 100100;  // 100.1 FM
    auto sel = make_selector_amfm(freq);

    // try tuning
    ProgramInfo infoCb = {};
    EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _)
        .Times(AnyNumber())
        .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
    auto result = mSession->tune(sel);

    // expect a failure if it's not supported
    if (!utils::isSupported(mProperties, sel)) {
        EXPECT_EQ(Result::NOT_SUPPORTED, result);
        return;
    }

    // expect a callback if it succeeds
    EXPECT_EQ(Result::OK, result);
    EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);

    // it should tune exactly to what was requested
    auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY);
    EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
}

/**
 * Test tuning with invalid selectors.
 *
 * Verifies that:
 *  - if the selector is not supported, it's ignored;
 *  - if it is supported, an invalid value results with INVALID_ARGUMENTS;
 */
TEST_F(BroadcastRadioHalTest, TuneFailsWithInvalid) {
    ASSERT_TRUE(openSession());

    vector<ProgramIdentifier> invalid = {
        make_identifier(IdentifierType::AMFM_FREQUENCY, 0),
        make_identifier(IdentifierType::RDS_PI, 0x10000),
        make_identifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
        make_identifier(IdentifierType::DAB_SID_EXT, 0),
        make_identifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
        make_identifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
    };

    for (auto&& id : invalid) {
        ProgramSelector sel{id, {}};

        auto result = mSession->tune(sel);

        if (utils::isSupported(mProperties, sel)) {
            EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
        } else {
            EXPECT_EQ(Result::NOT_SUPPORTED, result);
        }
    }
}

/**
 * Test tuning with empty program selector.
 *
 * Verifies that:
 *  - tune fails with NOT_SUPPORTED when program selector is not initialized.
 */
TEST_F(BroadcastRadioHalTest, TuneFailsWithEmpty) {
    ASSERT_TRUE(openSession());

    // Program type is 1-based, so 0 will always be invalid.
    ProgramSelector sel = {};
    auto result = mSession->tune(sel);
    ASSERT_EQ(Result::NOT_SUPPORTED, result);
}

/**
 * Test scanning to next/prev station.
 *
 * Verifies that:
 *  - the method succeeds;
 *  - the program info is changed within timeout::tune;
 *  - works both directions and with or without skipping sub-channel.
 */
TEST_F(BroadcastRadioHalTest, Scan) {
    ASSERT_TRUE(openSession());

    EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
    auto result = mSession->scan(true /* up */, true /* skip subchannel */);
    EXPECT_EQ(Result::OK, result);
    EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);

    EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
    result = mSession->scan(false /* down */, false /* don't skip subchannel */);
    EXPECT_EQ(Result::OK, result);
    EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
}

/**
 * Test step operation.
 *
 * Verifies that:
 *  - the method succeeds or returns NOT_SUPPORTED;
 *  - the program info is changed within timeout::tune if the method succeeded;
 *  - works both directions.
 */
TEST_F(BroadcastRadioHalTest, Step) {
    ASSERT_TRUE(openSession());

    EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _).Times(AnyNumber());
    auto result = mSession->step(true /* up */);
    if (result == Result::NOT_SUPPORTED) return;
    EXPECT_EQ(Result::OK, result);
    EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);

    EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
    result = mSession->step(false /* down */);
    EXPECT_EQ(Result::OK, result);
    EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
}

/**
 * Test tune cancellation.
 *
 * Verifies that:
 *  - the method does not crash after being invoked multiple times.
 */
TEST_F(BroadcastRadioHalTest, Cancel) {
    ASSERT_TRUE(openSession());

    for (int i = 0; i < 10; i++) {
        auto scanResult = mSession->scan(true /* up */, true /* skip subchannel */);
        ASSERT_EQ(Result::OK, scanResult);

        auto cancelResult = mSession->cancel();
        ASSERT_TRUE(cancelResult.isOk());
    }
}

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

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

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

    wasCalled = false;
    hidlResult = mSession->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_F(BroadcastRadioHalTest, UnknownParameters) {
    ASSERT_TRUE(openSession());

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

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

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

/**
 * Test session closing.
 *
 * Verifies that:
 *  - the method does not crash after being invoked multiple times.
 */
TEST_F(BroadcastRadioHalTest, Close) {
    ASSERT_TRUE(openSession());

    for (int i = 0; i < 10; i++) {
        auto cancelResult = mSession->close();
        ASSERT_TRUE(cancelResult.isOk());
    }
}

/**
 * Test geting image of invalid ID.
 *
 * Verifies that:
 * - getImage call handles argument 0 gracefully.
 */
TEST_F(BroadcastRadioHalTest, GetNoImage) {
    size_t len = 0;
    auto result = mModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });

    ASSERT_TRUE(result.isOk());
    ASSERT_EQ(0u, len);
}

}  // namespace vts
}  // namespace V2_0
}  // 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;
}
Loading