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

Commit f6f6890a authored by Weilin Xu's avatar Weilin Xu Committed by Android (Google) Code Review
Browse files

Merge "Unit test for radio announcement and callback"

parents fd495110 e9eea51a
Loading
Loading
Loading
Loading
+33 −2
Original line number Diff line number Diff line
@@ -15,9 +15,11 @@
 */
package com.android.server.broadcastradio.aidl;

import android.hardware.broadcastradio.IdentifierType;
import android.hardware.broadcastradio.Metadata;
import android.hardware.broadcastradio.ProgramIdentifier;
import android.hardware.broadcastradio.ProgramInfo;
import android.hardware.broadcastradio.VendorKeyValue;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioMetadata;
@@ -42,7 +44,7 @@ final class AidlTestUtils {
        return makeProgramInfo(selector, signalQuality);
    }

    static ProgramSelector makeFMSelector(long freq) {
    static ProgramSelector makeFmSelector(long freq) {
        return makeProgramSelector(ProgramSelector.PROGRAM_TYPE_FM,
                new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY,
                        freq));
@@ -54,6 +56,18 @@ final class AidlTestUtils {
                /* vendorIds= */ null);
    }

    static android.hardware.broadcastradio.ProgramSelector makeHalFmSelector(int freq) {
        ProgramIdentifier halId = new ProgramIdentifier();
        halId.type = IdentifierType.AMFM_FREQUENCY_KHZ;
        halId.value = freq;

        android.hardware.broadcastradio.ProgramSelector halSelector =
                new android.hardware.broadcastradio.ProgramSelector();
        halSelector.primaryId = halId;
        halSelector.secondaryIds = new ProgramIdentifier[0];
        return halSelector;
    }

    static ProgramInfo programInfoToHalProgramInfo(RadioManager.ProgramInfo info) {
        // Note that because ConversionUtils does not by design provide functions for all
        // conversions, this function only copies fields that are set by makeProgramInfo().
@@ -69,7 +83,7 @@ final class AidlTestUtils {
        return hwInfo;
    }

    static ProgramInfo makeHalProgramSelector(
    static ProgramInfo makeHalProgramInfo(
            android.hardware.broadcastradio.ProgramSelector hwSel, int hwSignalQuality) {
        ProgramInfo hwInfo = new ProgramInfo();
        hwInfo.selector = hwSel;
@@ -80,4 +94,21 @@ final class AidlTestUtils {
        hwInfo.metadata = new Metadata[]{};
        return hwInfo;
    }

    static VendorKeyValue makeVendorKeyValue(String vendorKey, String vendorValue) {
        VendorKeyValue vendorKeyValue = new VendorKeyValue();
        vendorKeyValue.key = vendorKey;
        vendorKeyValue.value = vendorValue;
        return vendorKeyValue;
    }

    static android.hardware.broadcastradio.Announcement makeAnnouncement(int type,
            int selectorFreq) {
        android.hardware.broadcastradio.Announcement halAnnouncement =
                new android.hardware.broadcastradio.Announcement();
        halAnnouncement.type = (byte) type;
        halAnnouncement.selector = makeHalFmSelector(selectorFreq);
        halAnnouncement.vendorInfo = new VendorKeyValue[]{};
        return halAnnouncement;
    }
}
+171 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.
 */

package com.android.server.broadcastradio.aidl;

import static com.google.common.truth.Truth.assertWithMessage;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.hardware.radio.Announcement;
import android.hardware.radio.IAnnouncementListener;
import android.hardware.radio.ICloseHandle;
import android.os.IBinder;
import android.os.RemoteException;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.util.Arrays;
import java.util.List;

/**
 * Tests for AIDL HAL AnnouncementAggregator.
 */
@RunWith(MockitoJUnitRunner.class)
public final class AnnouncementAggregatorTest {
    private static final int[] TEST_ENABLED_TYPES = new int[]{Announcement.TYPE_TRAFFIC};

    private final Object mLock = new Object();
    private AnnouncementAggregator mAnnouncementAggregator;
    private IBinder.DeathRecipient mDeathRecipient;

    @Mock
    private IAnnouncementListener mListenerMock;
    @Mock
    private IBinder mBinderMock;
    // Array of mocked radio modules
    private RadioModule[] mRadioModuleMocks;
    // Array of mocked close handles
    private ICloseHandle[] mCloseHandleMocks;
    // Array of mocked announcements
    private Announcement[] mAnnouncementMocks;

    @Before
    public void setUp() throws Exception {
        ArgumentCaptor<IBinder.DeathRecipient> deathRecipientCaptor =
                ArgumentCaptor.forClass(IBinder.DeathRecipient.class);
        when(mListenerMock.asBinder()).thenReturn(mBinderMock);

        mAnnouncementAggregator = new AnnouncementAggregator(mListenerMock, mLock);

        verify(mBinderMock).linkToDeath(deathRecipientCaptor.capture(), anyInt());
        mDeathRecipient = deathRecipientCaptor.getValue();
    }

    @Test
    public void onListUpdated_withOneModuleWatcher() throws Exception {
        ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor =
                ArgumentCaptor.forClass(IAnnouncementListener.class);
        watchModules(/* moduleNumber= */ 1);

        verify(mRadioModuleMocks[0]).addAnnouncementListener(moduleWatcherCaptor.capture(), any());

        moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[0]));

        verify(mListenerMock).onListUpdated(any());
    }

    @Test
    public void onListUpdated_withMultipleModuleWatchers() throws Exception {
        int moduleNumber = 3;
        watchModules(moduleNumber);

        for (int index = 0; index < moduleNumber; index++) {
            ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor =
                    ArgumentCaptor.forClass(IAnnouncementListener.class);
            ArgumentCaptor<List<Announcement>> announcementsCaptor =
                    ArgumentCaptor.forClass(List.class);
            verify(mRadioModuleMocks[index])
                    .addAnnouncementListener(moduleWatcherCaptor.capture(), any());

            moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index]));

            verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture());
            assertWithMessage("Number of announcements %s", announcementsCaptor.getValue())
                    .that(announcementsCaptor.getValue().size()).isEqualTo(index + 1);
        }
    }

    @Test
    public void close_withOneModuleWatcher_invokesCloseHandle() throws Exception {
        watchModules(/* moduleNumber= */ 1);

        mAnnouncementAggregator.close();

        verify(mCloseHandleMocks[0]).close();
        verify(mBinderMock).unlinkToDeath(eq(mDeathRecipient), anyInt());
    }

    @Test
    public void close_withMultipleModuleWatcher_invokesCloseHandles() throws Exception {
        int moduleNumber = 3;
        watchModules(moduleNumber);

        mAnnouncementAggregator.close();

        for (int index = 0; index < moduleNumber; index++) {
            verify(mCloseHandleMocks[index]).close();
        }
    }

    @Test
    public void close_twice_invokesCloseHandleOnce() throws Exception {
        watchModules(/* moduleNumber= */ 1);

        mAnnouncementAggregator.close();
        mAnnouncementAggregator.close();

        verify(mCloseHandleMocks[0]).close();
        verify(mBinderMock).unlinkToDeath(eq(mDeathRecipient), anyInt());
    }

    @Test
    public void binderDied_forDeathRecipient_invokesCloseHandle() throws Exception {
        watchModules(/* moduleNumber= */ 1);

        mDeathRecipient.binderDied();

        verify(mCloseHandleMocks[0]).close();

    }

    private void watchModules(int moduleNumber) throws RemoteException {
        mRadioModuleMocks = new RadioModule[moduleNumber];
        mCloseHandleMocks = new ICloseHandle[moduleNumber];
        mAnnouncementMocks = new Announcement[moduleNumber];

        for (int index = 0; index < moduleNumber; index++) {
            mRadioModuleMocks[index] = mock(RadioModule.class);
            mCloseHandleMocks[index] = mock(ICloseHandle.class);
            mAnnouncementMocks[index] = mock(Announcement.class);

            when(mRadioModuleMocks[index].addAnnouncementListener(any(), any()))
                    .thenReturn(mCloseHandleMocks[index]);
            mAnnouncementAggregator.watchModule(mRadioModuleMocks[index], TEST_ENABLED_TYPES);
        }
    }
}
+50 −1
Original line number Diff line number Diff line
@@ -21,11 +21,16 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.graphics.Bitmap;
import android.hardware.broadcastradio.IBroadcastRadio;
import android.hardware.radio.Announcement;
import android.hardware.radio.IAnnouncementListener;
import android.hardware.radio.ICloseHandle;
import android.hardware.radio.RadioManager;
import android.os.RemoteException;

@@ -41,13 +46,20 @@ import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public final class RadioModuleTest {

    private static final int TEST_ENABLED_TYPE = Announcement.TYPE_EVENT;

    // Mocks
    @Mock
    private IBroadcastRadio mBroadcastRadioMock;
    @Mock
    private IAnnouncementListener mListenerMock;
    @Mock
    private android.hardware.broadcastradio.ICloseHandle mHalCloseHandleMock;

    private final Object mLock = new Object();
    // RadioModule under test
    private RadioModule mRadioModule;
    private android.hardware.broadcastradio.IAnnouncementListener mHalListener;

    @Before
    public void setup() throws RemoteException {
@@ -62,6 +74,11 @@ public final class RadioModuleTest {

        // TODO(b/241118988): test non-null image for getImage method
        when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(null);
        doAnswer(invocation -> {
            mHalListener = (android.hardware.broadcastradio.IAnnouncementListener) invocation
                    .getArguments()[0];
            return null;
        }).when(mBroadcastRadioMock).registerAnnouncementListener(any(), any());
    }

    @Test
@@ -71,7 +88,7 @@ public final class RadioModuleTest {
    }

    @Test
    public void setInternalHalCallback_callbackSetInHal() throws RemoteException {
    public void setInternalHalCallback_callbackSetInHal() throws Exception {
        mRadioModule.setInternalHalCallback();

        verify(mBroadcastRadioMock).setTunerCallback(any());
@@ -97,4 +114,36 @@ public final class RadioModuleTest {
        assertWithMessage("Exception for getting image with invalid ID")
                .that(thrown).hasMessageThat().contains("Image ID is missing");
    }

    @Test
    public void addAnnouncementListener_listenerRegistered() throws Exception {
        mRadioModule.addAnnouncementListener(mListenerMock, new int[]{TEST_ENABLED_TYPE});

        verify(mBroadcastRadioMock)
                .registerAnnouncementListener(any(), eq(new byte[]{TEST_ENABLED_TYPE}));
    }

    @Test
    public void onListUpdate_forAnnouncementListener() throws Exception {
        android.hardware.broadcastradio.Announcement halAnnouncement =
                AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, /* selectorFreq= */ 96300);
        mRadioModule.addAnnouncementListener(mListenerMock, new int[]{TEST_ENABLED_TYPE});

        mHalListener.onListUpdated(
                new android.hardware.broadcastradio.Announcement[]{halAnnouncement});

        verify(mListenerMock).onListUpdated(any());
    }

    @Test
    public void close_forCloseHandle() throws Exception {
        when(mBroadcastRadioMock.registerAnnouncementListener(any(), any()))
                .thenReturn(mHalCloseHandleMock);
        ICloseHandle closeHandle =
                mRadioModule.addAnnouncementListener(mListenerMock, new int[]{TEST_ENABLED_TYPE});

        closeHandle.close();

        verify(mHalCloseHandleMock).close();
    }
}
+175 −102

File changed.

Preview size limit exceeded, changes collapsed.