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

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

Fix remaining instrumentation tests.

Bug: 69958777
Test: VTS, Instrumentation
Change-Id: I98956ea4b1d2953b9159fb7d53ce61e57f80a90f
parent 43a35464
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -112,6 +112,12 @@ Return<void> BroadcastRadio::getDabRegionConfig(getDabRegionConfig_cb _hidl_cb)
Return<void> BroadcastRadio::openSession(const sp<ITunerCallback>& callback,
                                         openSession_cb _hidl_cb) {
    ALOGV("%s", __func__);

    /* For the needs of default implementation it's fine to instantiate new session object
     * out of the lock scope. If your implementation needs it, use reentrant lock.
     */
    sp<TunerSession> newSession = new TunerSession(*this, callback);

    lock_guard<mutex> lk(mMut);

    auto oldSession = mSession.promote();
@@ -121,7 +127,6 @@ Return<void> BroadcastRadio::openSession(const sp<ITunerCallback>& callback,
        mSession = nullptr;
    }

    sp<TunerSession> newSession = new TunerSession(*this, callback);
    mSession = newSession;

    _hidl_cb(Result::OK, newSession);
+30 −5
Original line number Diff line number Diff line
@@ -50,7 +50,12 @@ static constexpr auto list = 1s;
}  // namespace delay

TunerSession::TunerSession(BroadcastRadio& module, const sp<ITunerCallback>& callback)
    : mCallback(callback), mModule(module) {}
    : mCallback(callback), mModule(module) {
    auto&& ranges = module.getAmFmConfig().ranges;
    if (ranges.size() > 0) {
        tuneInternalLocked(utils::make_selector_amfm(ranges[0].lowerBound));
    }
}

// makes ProgramInfo that points to no program
static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) {
@@ -63,6 +68,8 @@ static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) {
}

void TunerSession::tuneInternalLocked(const ProgramSelector& sel) {
    ALOGV("%s(%s)", __func__, toString(sel).c_str());

    VirtualProgram virtualProgram;
    ProgramInfo programInfo;
    if (virtualRadio().getProgram(sel, virtualProgram)) {
@@ -100,6 +107,8 @@ Return<Result> TunerSession::tune(const ProgramSelector& sel) {
        return Result::INVALID_ARGUMENTS;
    }

    cancelLocked();

    mIsTuneCompleted = false;
    auto task = [this, sel]() {
        lock_guard<mutex> lk(mMut);
@@ -115,6 +124,8 @@ Return<Result> TunerSession::scan(bool directionUp, bool /* skipSubChannel */) {
    lock_guard<mutex> lk(mMut);
    if (mIsClosed) return Result::INVALID_STATE;

    cancelLocked();

    auto list = virtualRadio().getProgramList();

    if (list.empty()) {
@@ -166,13 +177,13 @@ Return<Result> TunerSession::step(bool directionUp) {
    lock_guard<mutex> lk(mMut);
    if (mIsClosed) return Result::INVALID_STATE;

    cancelLocked();

    if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY)) {
        ALOGE("Can't step in anything else than AM/FM");
        return Result::NOT_SUPPORTED;
    }

    mIsTuneCompleted = false;

    auto stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY);
    auto range = getAmFmRangeLocked();
    if (!range) {
@@ -188,6 +199,7 @@ Return<Result> TunerSession::step(bool directionUp) {
    if (stepTo > range->upperBound) stepTo = range->lowerBound;
    if (stepTo < range->lowerBound) stepTo = range->upperBound;

    mIsTuneCompleted = false;
    auto task = [this, stepTo]() {
        ALOGI("Performing step to %s", std::to_string(stepTo).c_str());

@@ -200,12 +212,22 @@ Return<Result> TunerSession::step(bool directionUp) {
    return Result::OK;
}

void TunerSession::cancelLocked() {
    ALOGV("%s", __func__);

    mThread.cancelAll();
    if (utils::getType(mCurrentProgram.primaryId) != IdentifierType::INVALID) {
        mIsTuneCompleted = true;
    }
}

Return<void> TunerSession::cancel() {
    ALOGV("%s", __func__);
    lock_guard<mutex> lk(mMut);
    if (mIsClosed) return {};

    mThread.cancelAll();
    cancelLocked();

    return {};
}

@@ -281,7 +303,10 @@ Return<void> TunerSession::close() {
}

std::optional<AmFmBandRange> TunerSession::getAmFmRangeLocked() const {
    if (!mIsTuneCompleted) return {};
    if (!mIsTuneCompleted) {
        ALOGW("tune operation in process");
        return {};
    }
    if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY)) return {};

    auto freq = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY);
+1 −0
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ struct TunerSession : public ITunerSession {
    bool mIsTuneCompleted = false;
    ProgramSelector mCurrentProgram = {};

    void cancelLocked();
    void tuneInternalLocked(const ProgramSelector& sel);
    const VirtualRadio& virtualRadio() const;
    const BroadcastRadio& module() const;
+33 −2
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
 */

#define LOG_TAG "BcRadio.vts"
#define LOG_NDEBUG 0
#define EGMOCK_VERBOSE 1

#include <VtsHalHidlTargetTestBase.h>
#include <android-base/logging.h>
@@ -113,7 +115,15 @@ static void printSkipped(std::string msg) {
    std::cout << "[  SKIPPED ] " << msg << std::endl;
}

MATCHER_P(InfoHasId, id,
          std::string(negation ? "does not contain" : "contains") + " " + toString(id)) {
    auto ids = utils::getAllIds(arg.selector, utils::getType(id));
    return ids.end() != find(ids.begin(), ids.end(), id.value);
}

TunerCallbackMock::TunerCallbackMock() {
    EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChanged, _).Times(AnyNumber());

    // we expect the antenna is connected through the whole test
    EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
}
@@ -362,9 +372,19 @@ TEST_F(BroadcastRadioHalTest, FmTune) {
    uint64_t freq = 100100;  // 100.1 FM
    auto sel = make_selector_amfm(freq);

    /* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged
     * callback setting infoCb, because egmock cannot distinguish calls with different matchers
     * (there is one here and one in callback constructor).
     *
     * This sleep workaround will fix default implementation, but the real HW tests will still be
     * flaky. We probably need to implement egmock alternative based on actions.
     */
    std::this_thread::sleep_for(100ms);

    // try tuning
    ProgramInfo infoCb = {};
    EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _)
    EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged,
                        InfoHasId(utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq)))
        .Times(AnyNumber())
        .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
    auto result = mSession->tune(sel);
@@ -379,6 +399,8 @@ TEST_F(BroadcastRadioHalTest, FmTune) {
    EXPECT_EQ(Result::OK, result);
    EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);

    ALOGD("current program info: %s", toString(infoCb).c_str());

    // 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));
@@ -442,6 +464,9 @@ TEST_F(BroadcastRadioHalTest, TuneFailsWithEmpty) {
TEST_F(BroadcastRadioHalTest, Scan) {
    ASSERT_TRUE(openSession());

    // TODO(b/69958777): see FmTune workaround
    std::this_thread::sleep_for(100ms);

    EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
    auto result = mSession->scan(true /* up */, true /* skip subchannel */);
    EXPECT_EQ(Result::OK, result);
@@ -464,9 +489,15 @@ TEST_F(BroadcastRadioHalTest, Scan) {
TEST_F(BroadcastRadioHalTest, Step) {
    ASSERT_TRUE(openSession());

    // TODO(b/69958777): see FmTune workaround
    std::this_thread::sleep_for(100ms);

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

+21 −3
Original line number Diff line number Diff line
@@ -13,12 +13,27 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_MOCK_TIMEOUT
#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_MOCK_TIMEOUT
#ifndef ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT
#define ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT

#include <gmock/gmock.h>
#include <thread>

#ifndef EGMOCK_VERBOSE
#define EGMOCK_VERBOSE 0
#endif

/**
 * Print log message.
 *
 * INTERNAL IMPLEMENTATION - don't use in user code.
 */
#if EGMOCK_VERBOSE
#define EGMOCK_LOG_(...) ALOGV("egmock: " __VA_ARGS__)
#else
#define EGMOCK_LOG_(...)
#endif

/**
 * Common helper objects for gmock timeout extension.
 *
@@ -61,6 +76,7 @@ inline void EGMockFlippedComma_(std::function<void()> returned, std::function<vo
    auto invokeMock = [&]() { return egmock_##Method(__VA_ARGS__); }; \
    auto notify = [&]() {                                             \
        std::lock_guard<std::mutex> lk(egmock_mut_##Method);          \
        EGMOCK_LOG_(#Method " called");                               \
        egmock_called_##Method = true;                                \
        egmock_cond_##Method.notify_all();                            \
    };                                                                \
@@ -105,6 +121,7 @@ inline void EGMockFlippedComma_(std::function<void()> returned, std::function<vo
 *     EXPECT_TIMEOUT_CALL(account, charge, 100, Currency::USD);
 */
#define EXPECT_TIMEOUT_CALL(obj, Method, ...) \
    EGMOCK_LOG_(#Method " expected to call"); \
    (obj).egmock_called_##Method = false;     \
    EXPECT_CALL(obj, egmock_##Method(__VA_ARGS__))

@@ -124,6 +141,7 @@ inline void EGMockFlippedComma_(std::function<void()> returned, std::function<vo
 */
#define EXPECT_TIMEOUT_CALL_WAIT(obj, Method, timeout)                      \
    {                                                                       \
        EGMOCK_LOG_("waiting for " #Method " call");                        \
        std::unique_lock<std::mutex> lk((obj).egmock_mut_##Method);         \
        if (!(obj).egmock_called_##Method) {                                \
            auto status = (obj).egmock_cond_##Method.wait_for(lk, timeout); \
@@ -131,4 +149,4 @@ inline void EGMockFlippedComma_(std::function<void()> returned, std::function<vo
        }                                                                   \
    }

#endif  // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_MOCK_TIMEOUT
#endif  // ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT