Add unit tests for radio HIDL tuner and module

Unit tests for radio module and get/set methods in tuner session
of broadcast radio HIDL HAL client are added. Meanwhile, replace
deprecated Matchers package with ArgumentMatchers package.

Bug: 258034192
Test: atest TunerSessionHidlTest RadioTunerHidlTest
Change-Id: I5530c5b42cf3f4763df8d003871196d630ef06cd
This commit is contained in:
Weilin Xu
2022-11-09 03:37:29 +00:00
parent f4c573cc63
commit 65436d48fb
11 changed files with 361 additions and 40 deletions

View File

@@ -20,9 +20,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

View File

@@ -18,9 +18,9 @@ package com.android.server.broadcastradio;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;

View File

@@ -31,6 +31,16 @@ final class AidlTestUtils {
throw new UnsupportedOperationException("AidlTestUtils class is noninstantiable");
}
static RadioManager.ModuleProperties makeDefaultModuleProperties() {
return new RadioManager.ModuleProperties(
/* id= */ 0, /* serviceName= */ "", /* classId= */ 0, /* implementor= */ "",
/* product= */ "", /* version= */ "", /* serial= */ "", /* numTuners= */ 0,
/* numAudioSources= */ 0, /* isInitializationRequired= */ false,
/* isCaptureSupported= */ false, /* bands= */ null,
/* isBgScanSupported= */ false, new int[] {}, new int[] {},
/* dabFrequencyTable= */ null, /* vendorInfo= */ null);
}
static RadioManager.ProgramInfo makeProgramInfo(ProgramSelector selector, int signalQuality) {
return new RadioManager.ProgramInfo(selector,
selector.getPrimaryId(), selector.getPrimaryId(), /* relatedContents= */ null,

View File

@@ -19,9 +19,9 @@ package com.android.server.broadcastradio.aidl;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -48,14 +48,7 @@ public final class RadioModuleTest {
private static final int TEST_ENABLED_TYPE = Announcement.TYPE_EVENT;
private static final RadioManager.ModuleProperties TEST_MODULE_PROPERTIES =
new RadioManager.ModuleProperties(/* id= */ 0, /* serviceName= */ "", /* classId= */ 0,
/* implementor= */ "", /* product= */ "", /* version= */ "",
/* serial= */ "", /* numTuners= */ 0, /* numAudioSources= */ 0,
/* isInitializationRequired= */ false, /* isCaptureSupported= */ false,
/* bands= */ null, /* isBgScanSupported= */ false,
/* supportedProgramTypes= */ new int[]{},
/* supportedIdentifierTypes */ new int[]{},
/* dabFrequencyTable= */ null, /* vendorInfo= */ null);
AidlTestUtils.makeDefaultModuleProperties();
// Mocks
@Mock
@@ -108,7 +101,7 @@ public final class RadioModuleTest {
Bitmap imageTest = mRadioModule.getImage(imageId);
assertWithMessage("Image got from radio module").that(imageTest).isNull();
assertWithMessage("Image from radio module").that(imageTest).isNull();
}
@Test

View File

@@ -19,10 +19,10 @@ package com.android.server.broadcastradio.aidl;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
@@ -93,13 +93,8 @@ public final class TunerSessionTest {
@Before
public void setup() throws Exception {
mRadioModule = new RadioModule(mBroadcastRadioMock, new RadioManager.ModuleProperties(
/* id= */ 0, /* serviceName= */ "", /* classId= */ 0, /* implementor= */ "",
/* product= */ "", /* version= */ "", /* serial= */ "", /* numTuners= */ 0,
/* numAudioSources= */ 0, /* isInitializationRequired= */ false,
/* isCaptureSupported= */ false, /* bands= */ null, /* isBgScanSupported= */ false,
new int[] {}, new int[] {},
/* dabFrequencyTable= */ null, /* vendorInfo= */ null), mLock);
mRadioModule = new RadioModule(mBroadcastRadioMock,
AidlTestUtils.makeDefaultModuleProperties(), mLock);
doAnswer(invocation -> {
mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
@@ -424,7 +419,7 @@ public final class TunerSessionTest {
mTunerSessions[0].getImage(imageId);
});
assertWithMessage("Exception for getting image with invalid ID")
assertWithMessage("Get image exception")
.that(thrown).hasMessageThat().contains("Image ID is missing");
}
@@ -467,7 +462,7 @@ public final class TunerSessionTest {
boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag);
verify(mBroadcastRadioMock).isConfigFlagSet(flag);
assertWithMessage("Config flag %s is supported", flag).that(isSupported).isFalse();
assertWithMessage("Config flag %s is supported", flag).that(isSupported).isFalse();
}
@Test

View File

@@ -0,0 +1,151 @@
/*
* 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.hal2;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.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.V2_0.Constants;
import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
import android.hardware.broadcastradio.V2_0.Result;
import android.hardware.radio.Announcement;
import android.hardware.radio.IAnnouncementListener;
import android.hardware.radio.ICloseHandle;
import android.hardware.radio.RadioManager;
import android.os.RemoteException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Tests for HIDL HAL RadioModule.
*/
@RunWith(MockitoJUnitRunner.class)
public final class RadioModuleHidlTest {
private static final int TEST_ENABLED_TYPE = Announcement.TYPE_EVENT;
private static final RadioManager.ModuleProperties TEST_MODULE_PROPERTIES =
TestUtils.makeDefaultModuleProperties();
@Mock
private IBroadcastRadio mBroadcastRadioMock;
@Mock
private IAnnouncementListener mListenerMock;
@Mock
private android.hardware.broadcastradio.V2_0.ICloseHandle mHalCloseHandleMock;
private final Object mLock = new Object();
private RadioModule mRadioModule;
private android.hardware.broadcastradio.V2_0.IAnnouncementListener mHalListener;
@Before
public void setup() throws RemoteException {
mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES, mLock);
when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(new ArrayList<Byte>(0));
doAnswer(invocation -> {
mHalListener = (android.hardware.broadcastradio.V2_0.IAnnouncementListener) invocation
.getArguments()[1];
IBroadcastRadio.registerAnnouncementListenerCallback cb =
(IBroadcastRadio.registerAnnouncementListenerCallback)
invocation.getArguments()[2];
cb.onValues(Result.OK, mHalCloseHandleMock);
return null;
}).when(mBroadcastRadioMock).registerAnnouncementListener(any(), any(), any());
}
@Test
public void getService() {
assertWithMessage("Service of radio module")
.that(mRadioModule.getService()).isEqualTo(mBroadcastRadioMock);
}
@Test
public void getProperties() {
assertWithMessage("Module properties of radio module")
.that(mRadioModule.getProperties()).isEqualTo(TEST_MODULE_PROPERTIES);
}
@Test
public void getImage_withValidIdFromRadioModule() {
int imageId = 1;
Bitmap imageTest = mRadioModule.getImage(imageId);
assertWithMessage("Image from radio module").that(imageTest).isNull();
}
@Test
public void getImage_withInvalidIdFromRadioModule_throwsIllegalArgumentException() {
int invalidImageId = Constants.INVALID_IMAGE;
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> {
mRadioModule.getImage(invalidImageId);
});
assertWithMessage("Exception for getting image with invalid ID")
.that(thrown).hasMessageThat().contains("Image ID is missing");
}
@Test
public void addAnnouncementListener_listenerRegistered() throws Exception {
ArrayList<Byte> enabledListExpected = new ArrayList<Byte>(Arrays.asList(
(byte) TEST_ENABLED_TYPE));
mRadioModule.addAnnouncementListener(new int[]{TEST_ENABLED_TYPE}, mListenerMock);
verify(mBroadcastRadioMock)
.registerAnnouncementListener(eq(enabledListExpected), any(), any());
}
@Test
public void onListUpdate_forAnnouncementListener() throws Exception {
android.hardware.broadcastradio.V2_0.Announcement halAnnouncement =
TestUtils.makeAnnouncement(TEST_ENABLED_TYPE, /* selectorFreq= */ 96300);
mRadioModule.addAnnouncementListener(new int[]{TEST_ENABLED_TYPE}, mListenerMock);
mHalListener.onListUpdated(
new ArrayList<android.hardware.broadcastradio.V2_0.Announcement>(
Arrays.asList(halAnnouncement)));
verify(mListenerMock).onListUpdated(any());
}
@Test
public void close_forCloseHandle() throws Exception {
ICloseHandle closeHandle =
mRadioModule.addAnnouncementListener(new int[]{TEST_ENABLED_TYPE}, mListenerMock);
closeHandle.close();
verify(mHalCloseHandleMock).close();
}
}

View File

@@ -95,9 +95,8 @@ public class StartProgramListUpdatesFanoutTest {
public void setup() throws RemoteException {
MockitoAnnotations.initMocks(this);
mRadioModule = new RadioModule(mBroadcastRadioMock, new RadioManager.ModuleProperties(0, "",
0, "", "", "", "", 0, 0, false, false, null, false, new int[] {}, new int[] {},
null, null), mLock);
mRadioModule = new RadioModule(mBroadcastRadioMock,
TestUtils.makeDefaultModuleProperties(), mLock);
doAnswer((Answer) invocation -> {
mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];

View File

@@ -33,6 +33,16 @@ final class TestUtils {
throw new UnsupportedOperationException("TestUtils class is noninstantiable");
}
static RadioManager.ModuleProperties makeDefaultModuleProperties() {
return new RadioManager.ModuleProperties(
/* id= */ 0, /* serviceName= */ "", /* classId= */ 0, /* implementor= */ "",
/* product= */ "", /* version= */ "", /* serial= */ "", /* numTuners= */ 0,
/* numAudioSources= */ 0, /* isInitializationRequired= */ false,
/* isCaptureSupported= */ false, /* bands= */ null,
/* isBgScanSupported= */ false, new int[] {}, new int[] {},
/* dabFrequencyTable= */ null, /* vendorInfo= */ null);
}
static RadioManager.ProgramInfo makeProgramInfo(ProgramSelector selector, int signalQuality) {
return new RadioManager.ProgramInfo(selector,
selector.getPrimaryId(), selector.getPrimaryId(), /* relatedContents= */ null,

View File

@@ -37,12 +37,15 @@ import android.hardware.broadcastradio.V2_0.ITunerSession;
import android.hardware.broadcastradio.V2_0.IdentifierType;
import android.hardware.broadcastradio.V2_0.ProgramInfo;
import android.hardware.broadcastradio.V2_0.Result;
import android.hardware.broadcastradio.V2_0.VendorKeyValue;
import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioTuner;
import android.util.ArrayMap;
import android.util.ArraySet;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -51,6 +54,8 @@ import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.verification.VerificationWithTimeout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
/**
* Tests for HIDL HAL TunerSession.
@@ -73,6 +78,7 @@ public final class TunerSessionHidlTest {
private static final int UNSUPPORTED_CONFIG_FLAG = 0;
private final Object mLock = new Object();
private final ArrayMap<Integer, Boolean> mHalConfigMap = new ArrayMap<>();
private RadioModule mRadioModule;
private ITunerCallback mHalTunerCallback;
private ProgramInfo mHalCurrentInfo;
@@ -84,13 +90,8 @@ public final class TunerSessionHidlTest {
@Before
public void setup() throws Exception {
mRadioModule = new RadioModule(mBroadcastRadioMock, new RadioManager.ModuleProperties(
/* id= */ 0, /* serviceName= */ "", /* classId= */ 0, /* implementor= */ "",
/* product= */ "", /* version= */ "", /* serial= */ "", /* numTuners= */ 0,
/* numAudioSources= */ 0, /* isInitializationRequired= */ false,
/* isCaptureSupported= */ false, /* bands= */ null, /* isBgScanSupported= */ false,
new int[] {}, new int[] {},
/* dabFrequencyTable= */ null, /* vendorInfo= */ null), mLock);
mRadioModule = new RadioModule(mBroadcastRadioMock,
TestUtils.makeDefaultModuleProperties(), mLock);
doAnswer(invocation -> {
mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
@@ -142,6 +143,32 @@ public final class TunerSessionHidlTest {
}).when(mHalTunerSessionMock).scan(anyBoolean(), anyBoolean());
when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(new ArrayList<Byte>(0));
doAnswer(invocation -> {
int configFlag = (int) invocation.getArguments()[0];
ITunerSession.isConfigFlagSetCallback cb = (ITunerSession.isConfigFlagSetCallback)
invocation.getArguments()[1];
if (configFlag == UNSUPPORTED_CONFIG_FLAG) {
cb.onValues(Result.NOT_SUPPORTED, false);
return null;
}
cb.onValues(Result.OK, mHalConfigMap.getOrDefault(configFlag, false));
return null;
}).when(mHalTunerSessionMock).isConfigFlagSet(anyInt(), any());
doAnswer(invocation -> {
int configFlag = (int) invocation.getArguments()[0];
if (configFlag == UNSUPPORTED_CONFIG_FLAG) {
return Result.NOT_SUPPORTED;
}
mHalConfigMap.put(configFlag, (boolean) invocation.getArguments()[1]);
return Result.OK;
}).when(mHalTunerSessionMock).setConfigFlag(anyInt(), anyBoolean());
}
@After
public void cleanUp() {
mHalConfigMap.clear();
}
@Test
@@ -395,7 +422,7 @@ public final class TunerSessionHidlTest {
mTunerSessions[0].getImage(imageId);
});
assertWithMessage("Exception for getting image with invalid ID")
assertWithMessage("Get image exception")
.that(thrown).hasMessageThat().contains("Image ID is missing");
}
@@ -430,6 +457,138 @@ public final class TunerSessionHidlTest {
verify(mHalTunerSessionMock).stopProgramListUpdates();
}
@Test
public void isConfigFlagSupported_withUnsupportedFlag_returnsFalse() throws Exception {
openAidlClients(/* numClients= */ 1);
int flag = UNSUPPORTED_CONFIG_FLAG;
boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag);
verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any());
assertWithMessage("Config flag %s is supported", flag).that(isSupported).isFalse();
}
@Test
public void isConfigFlagSupported_withSupportedFlag_returnsTrue() throws Exception {
openAidlClients(/* numClients= */ 1);
int flag = UNSUPPORTED_CONFIG_FLAG + 1;
boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag);
verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any());
assertWithMessage("Config flag %s is supported", flag).that(isSupported).isTrue();
}
@Test
public void setConfigFlag_withUnsupportedFlag_throwsRuntimeException() throws Exception {
openAidlClients(/* numClients= */ 1);
int flag = UNSUPPORTED_CONFIG_FLAG;
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
mTunerSessions[0].setConfigFlag(flag, /* value= */ true);
});
assertWithMessage("Exception for setting unsupported flag %s", flag)
.that(thrown).hasMessageThat().contains("setConfigFlag: NOT_SUPPORTED");
}
@Test
public void setConfigFlag_withFlagSetToTrue() throws Exception {
openAidlClients(/* numClients= */ 1);
int flag = UNSUPPORTED_CONFIG_FLAG + 1;
mTunerSessions[0].setConfigFlag(flag, /* value= */ true);
verify(mHalTunerSessionMock).setConfigFlag(flag, /* value= */ true);
}
@Test
public void setConfigFlag_withFlagSetToFalse() throws Exception {
openAidlClients(/* numClients= */ 1);
int flag = UNSUPPORTED_CONFIG_FLAG + 1;
mTunerSessions[0].setConfigFlag(flag, /* value= */ false);
verify(mHalTunerSessionMock).setConfigFlag(flag, /* value= */ false);
}
@Test
public void isConfigFlagSet_withUnsupportedFlag_throwsRuntimeException()
throws Exception {
openAidlClients(/* numClients= */ 1);
int flag = UNSUPPORTED_CONFIG_FLAG;
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
mTunerSessions[0].isConfigFlagSet(flag);
});
assertWithMessage("Exception for check if unsupported flag %s is set", flag)
.that(thrown).hasMessageThat().contains("isConfigFlagSet: NOT_SUPPORTED");
}
@Test
public void isConfigFlagSet_withSupportedFlag() throws Exception {
openAidlClients(/* numClients= */ 1);
int flag = UNSUPPORTED_CONFIG_FLAG + 1;
boolean expectedConfigFlagValue = true;
mTunerSessions[0].setConfigFlag(flag, /* value= */ expectedConfigFlagValue);
boolean isSet = mTunerSessions[0].isConfigFlagSet(flag);
assertWithMessage("Config flag %s is set", flag)
.that(isSet).isEqualTo(expectedConfigFlagValue);
}
@Test
public void setParameters_withMockParameters() throws Exception {
openAidlClients(/* numClients= */ 1);
Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1",
"mockParam2", "mockValue2");
mTunerSessions[0].setParameters(parametersSet);
verify(mHalTunerSessionMock).setParameters(Convert.vendorInfoToHal(parametersSet));
}
@Test
public void getParameters_withMockKeys() throws Exception {
openAidlClients(/* numClients= */ 1);
ArrayList<String> parameterKeys = new ArrayList<>(Arrays.asList("mockKey1", "mockKey2"));
mTunerSessions[0].getParameters(parameterKeys);
verify(mHalTunerSessionMock).getParameters(parameterKeys);
}
@Test
public void onConfigFlagUpdated_forTunerCallback() throws Exception {
int numSessions = 3;
openAidlClients(numSessions);
mHalTunerCallback.onAntennaStateChange(/* connected= */ false);
for (int index = 0; index < numSessions; index++) {
verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT)
.onAntennaState(/* connected= */ false);
}
}
@Test
public void onParametersUpdated_forTunerCallback() throws Exception {
int numSessions = 3;
openAidlClients(numSessions);
ArrayList<VendorKeyValue> parametersUpdates = new ArrayList<VendorKeyValue>(Arrays.asList(
TestUtils.makeVendorKeyValue("com.vendor.parameter1", "value1")));
Map<String, String> parametersExpected = Map.of("com.vendor.parameter1", "value1");
mHalTunerCallback.onParametersUpdated(parametersUpdates);
for (int index = 0; index < numSessions; index++) {
verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT)
.onParametersUpdated(parametersExpected);
}
}
private void openAidlClients(int numClients) throws Exception {
mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients];
mTunerSessions = new TunerSession[numClients];

View File

@@ -135,7 +135,7 @@ public class BroadcastRadioService {
public @NonNull Collection<RadioManager.ModuleProperties> listModules() {
Slog.v(TAG, "List HIDL 2.0 modules");
synchronized (mLock) {
return mModules.values().stream().map(module -> module.mProperties)
return mModules.values().stream().map(module -> module.getProperties())
.collect(Collectors.toList());
}
}

View File

@@ -58,7 +58,7 @@ class RadioModule {
private static final int RADIO_EVENT_LOGGER_QUEUE_SIZE = 25;
@NonNull private final IBroadcastRadio mService;
@NonNull public final RadioManager.ModuleProperties mProperties;
@NonNull private final RadioManager.ModuleProperties mProperties;
private final Object mLock;
@NonNull private final Handler mHandler;
@@ -177,6 +177,10 @@ class RadioModule {
return mService;
}
public RadioManager.ModuleProperties getProperties() {
return mProperties;
}
public @NonNull TunerSession openSession(@NonNull android.hardware.radio.ITunerCallback userCb)
throws RemoteException {
mEventLogger.logRadioEvent("Open TunerSession");