Merge "Add unit tests for radio service implementations"
This commit is contained in:
@@ -48,7 +48,7 @@ android_test {
|
||||
],
|
||||
// mockito-target-inline dependency
|
||||
jni_libs: [
|
||||
"libcarservicejni",
|
||||
"libdexmakerjvmtiagent",
|
||||
"libstaticjvmtiagent",
|
||||
],
|
||||
}
|
||||
|
||||
32
core/tests/BroadcastRadioTests/AndroidTest.xml
Normal file
32
core/tests/BroadcastRadioTests/AndroidTest.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<configuration description="Runs Broadcast Radio Tests.">
|
||||
<option name="test-suite-tag" value="apct" />
|
||||
<option name="test-suite-tag" value="apct-instrumentation" />
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
|
||||
<option name="cleanup-apks" value="true" />
|
||||
<option name="test-file-name" value="BroadcastRadioTests.apk" />
|
||||
</target_preparer>
|
||||
|
||||
<option name="test-tag" value="BroadcastRadioTests" />
|
||||
|
||||
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
|
||||
<option name="package" value="android.hardware.radio.tests" />
|
||||
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
|
||||
<option name="hidden-api-checks" value="false"/>
|
||||
</test>
|
||||
</configuration>
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.mockito.MockitoSession;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
/**
|
||||
* Base class to make it easier to write tests that uses {@code ExtendedMockito} for radio.
|
||||
*
|
||||
*/
|
||||
public abstract class ExtendedRadioMockitoTestCase {
|
||||
|
||||
private static final String TAG = "RadioMockitoTestCase";
|
||||
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
private MockitoSession mSession;
|
||||
|
||||
@Before
|
||||
public void startSession() {
|
||||
StaticMockitoSessionBuilder builder = mockitoSession()
|
||||
.initMocks(this)
|
||||
.strictness(Strictness.LENIENT);
|
||||
initializeSession(builder);
|
||||
mSession = builder.startMocking();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the mockito session for radio test.
|
||||
*
|
||||
* <p>Typically used to define which classes should have static methods mocked or spied.
|
||||
*/
|
||||
protected void initializeSession(StaticMockitoSessionBuilder builder) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "initializeSession()");
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public final void finishSession() {
|
||||
if (mSession == null) {
|
||||
Log.w(TAG, "finishSession(): no session");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "finishSession()");
|
||||
}
|
||||
} finally {
|
||||
// mSession.finishMocking() must ALWAYS be called (hence the over-protective try/finally
|
||||
// statements), otherwise it would cause failures on future tests as mockito
|
||||
// cannot start a session when a previous one is not finished
|
||||
mSession.finishMocking();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server.broadcastradio;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
@@ -31,30 +33,36 @@ import android.hardware.radio.ICloseHandle;
|
||||
import android.hardware.radio.ITuner;
|
||||
import android.hardware.radio.ITunerCallback;
|
||||
import android.hardware.radio.RadioManager;
|
||||
import android.os.IBinder;
|
||||
import android.os.ServiceManager;
|
||||
|
||||
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
|
||||
import com.android.server.broadcastradio.aidl.BroadcastRadioServiceImpl;
|
||||
|
||||
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.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests for {@link android.hardware.radio.IRadioService} with AIDL HAL implementation
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public final class IRadioServiceAidlImplTest {
|
||||
public final class IRadioServiceAidlImplTest extends ExtendedRadioMockitoTestCase {
|
||||
|
||||
private static final int[] ENABLE_TYPES = new int[]{Announcement.TYPE_TRAFFIC};
|
||||
private static final String AM_FM_SERVICE_NAME =
|
||||
"android.hardware.broadcastradio.IBroadcastRadio/amfm";
|
||||
private static final String DAB_SERVICE_NAME =
|
||||
"android.hardware.broadcastradio.IBroadcastRadio/dab";
|
||||
|
||||
private IRadioServiceAidlImpl mAidlImpl;
|
||||
|
||||
@Mock
|
||||
private BroadcastRadioService mServiceMock;
|
||||
@Mock
|
||||
private IBinder mServiceBinderMock;
|
||||
@Mock
|
||||
private BroadcastRadioServiceImpl mHalMock;
|
||||
@Mock
|
||||
private RadioManager.ModuleProperties mModuleMock;
|
||||
@@ -73,7 +81,7 @@ public final class IRadioServiceAidlImplTest {
|
||||
public void setUp() throws Exception {
|
||||
doNothing().when(mServiceMock).enforcePolicyAccess();
|
||||
|
||||
when(mHalMock.listModules()).thenReturn(Arrays.asList(mModuleMock));
|
||||
when(mHalMock.listModules()).thenReturn(List.of(mModuleMock));
|
||||
when(mHalMock.openSession(anyInt(), any(), anyBoolean(), any()))
|
||||
.thenReturn(mTunerMock);
|
||||
when(mHalMock.addAnnouncementListener(any(), any())).thenReturn(mICloseHandle);
|
||||
@@ -81,11 +89,26 @@ public final class IRadioServiceAidlImplTest {
|
||||
mAidlImpl = new IRadioServiceAidlImpl(mServiceMock, mHalMock);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeSession(StaticMockitoSessionBuilder builder) {
|
||||
builder.spyStatic(ServiceManager.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getServicesNames_forAidlImpl() {
|
||||
doReturn(null).when(() -> ServiceManager.waitForDeclaredService(
|
||||
AM_FM_SERVICE_NAME));
|
||||
doReturn(mServiceBinderMock).when(() -> ServiceManager.waitForDeclaredService(
|
||||
DAB_SERVICE_NAME));
|
||||
|
||||
assertWithMessage("Names of services available")
|
||||
.that(IRadioServiceAidlImpl.getServicesNames()).containsExactly(DAB_SERVICE_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadModules_forAidlImpl() {
|
||||
assertWithMessage("Modules loaded in AIDL HAL")
|
||||
.that(mAidlImpl.listModules())
|
||||
.containsExactly(mModuleMock);
|
||||
.that(mAidlImpl.listModules()).containsExactly(mModuleMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,5 +128,4 @@ public final class IRadioServiceAidlImplTest {
|
||||
assertWithMessage("Close handle of announcement listener for HAL 2")
|
||||
.that(closeHandle).isEqualTo(mICloseHandle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
|
||||
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.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.hardware.broadcastradio.IBroadcastRadio;
|
||||
import android.hardware.radio.ITuner;
|
||||
import android.hardware.radio.ITunerCallback;
|
||||
import android.hardware.radio.RadioManager;
|
||||
import android.hardware.radio.RadioTuner;
|
||||
import android.os.IBinder;
|
||||
import android.os.IServiceCallback;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
|
||||
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
|
||||
import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTestCase {
|
||||
|
||||
private static final int FM_RADIO_MODULE_ID = 0;
|
||||
private static final int DAB_RADIO_MODULE_ID = 1;
|
||||
private static final ArrayList<String> SERVICE_LIST =
|
||||
new ArrayList<>(Arrays.asList("FmService", "DabService"));
|
||||
|
||||
private BroadcastRadioServiceImpl mBroadcastRadioService;
|
||||
private IBinder.DeathRecipient mFmDeathRecipient;
|
||||
|
||||
@Mock
|
||||
private RadioManager.ModuleProperties mFmModuleMock;
|
||||
@Mock
|
||||
private RadioManager.ModuleProperties mDabModuleMock;
|
||||
@Mock
|
||||
private RadioModule mFmRadioModuleMock;
|
||||
@Mock
|
||||
private RadioModule mDabRadioModuleMock;
|
||||
@Mock
|
||||
private IBroadcastRadio mFmHalServiceMock;
|
||||
@Mock
|
||||
private IBroadcastRadio mDabHalServiceMock;
|
||||
@Mock
|
||||
private IBinder mFmBinderMock;
|
||||
@Mock
|
||||
private IBinder mDabBinderMock;
|
||||
@Mock
|
||||
private TunerSession mFmTunerSessionMock;
|
||||
@Mock
|
||||
private ITunerCallback mTunerCallbackMock;
|
||||
|
||||
@Override
|
||||
protected void initializeSession(StaticMockitoSessionBuilder builder) {
|
||||
builder.spyStatic(ServiceManager.class)
|
||||
.spyStatic(RadioModule.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listModules_withMultipleServiceNames() throws Exception {
|
||||
createBroadcastRadioService();
|
||||
|
||||
assertWithMessage("Radio modules in AIDL broadcast radio HAL client")
|
||||
.that(mBroadcastRadioService.listModules())
|
||||
.containsExactly(mFmModuleMock, mDabModuleMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasModules_withIdFoundInModules() throws Exception {
|
||||
createBroadcastRadioService();
|
||||
|
||||
assertWithMessage("DAB radio module in AIDL broadcast radio HAL client")
|
||||
.that(mBroadcastRadioService.hasModule(DAB_RADIO_MODULE_ID)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasModules_withIdNotFoundInModules() throws Exception {
|
||||
createBroadcastRadioService();
|
||||
|
||||
assertWithMessage("Radio module of id not found in AIDL broadcast radio HAL client")
|
||||
.that(mBroadcastRadioService.hasModule(DAB_RADIO_MODULE_ID + 1)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAnyModules_withModulesExist() throws Exception {
|
||||
createBroadcastRadioService();
|
||||
|
||||
assertWithMessage("Any radio module in AIDL broadcast radio HAL client")
|
||||
.that(mBroadcastRadioService.hasAnyModules()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void openSession_withIdFound() throws Exception {
|
||||
createBroadcastRadioService();
|
||||
|
||||
ITuner session = mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID,
|
||||
/*legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock);
|
||||
|
||||
assertWithMessage("Session opened in FM radio module")
|
||||
.that(session).isEqualTo(mFmTunerSessionMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void openSession_withIdNotFound() throws Exception {
|
||||
createBroadcastRadioService();
|
||||
|
||||
ITuner session = mBroadcastRadioService.openSession(DAB_RADIO_MODULE_ID + 1,
|
||||
/*legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock);
|
||||
|
||||
assertWithMessage("Session opened with id not found").that(session).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void binderDied_forDeathRecipient() throws Exception {
|
||||
createBroadcastRadioService();
|
||||
|
||||
mFmDeathRecipient.binderDied();
|
||||
|
||||
verify(mFmRadioModuleMock).closeSessions(eq(RadioTuner.ERROR_HARDWARE_FAILURE));
|
||||
assertWithMessage("FM radio module after FM broadcast radio HAL service died")
|
||||
.that(mBroadcastRadioService.hasModule(FM_RADIO_MODULE_ID)).isFalse();
|
||||
}
|
||||
|
||||
private void createBroadcastRadioService() throws RemoteException {
|
||||
mockServiceManager();
|
||||
mBroadcastRadioService = new BroadcastRadioServiceImpl(SERVICE_LIST);
|
||||
}
|
||||
|
||||
private void mockServiceManager() throws RemoteException {
|
||||
doAnswer((Answer<Void>) invocation -> {
|
||||
String serviceName = (String) invocation.getArguments()[0];
|
||||
IServiceCallback serviceCallback = (IServiceCallback) invocation.getArguments()[1];
|
||||
IBinder mockBinder = serviceName.equals("FmService") ? mFmBinderMock : mDabBinderMock;
|
||||
serviceCallback.onRegistration(serviceName, mockBinder);
|
||||
return null;
|
||||
}).when(() -> ServiceManager.registerForNotifications(anyString(),
|
||||
any(IServiceCallback.class)));
|
||||
|
||||
doReturn(mFmRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
|
||||
eq(FM_RADIO_MODULE_ID), anyString(), any(IBinder.class), any(Object.class)));
|
||||
doReturn(mDabRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
|
||||
eq(DAB_RADIO_MODULE_ID), anyString(), any(IBinder.class), any(Object.class)));
|
||||
|
||||
when(mFmRadioModuleMock.getProperties()).thenReturn(mFmModuleMock);
|
||||
when(mDabRadioModuleMock.getProperties()).thenReturn(mDabModuleMock);
|
||||
|
||||
when(mFmRadioModuleMock.getService()).thenReturn(mFmHalServiceMock);
|
||||
when(mDabRadioModuleMock.getService()).thenReturn(mDabHalServiceMock);
|
||||
|
||||
when(mFmHalServiceMock.asBinder()).thenReturn(mFmBinderMock);
|
||||
when(mDabHalServiceMock.asBinder()).thenReturn(mDabBinderMock);
|
||||
|
||||
doAnswer(invocation -> {
|
||||
mFmDeathRecipient = (IBinder.DeathRecipient) invocation.getArguments()[0];
|
||||
return null;
|
||||
}).when(mFmBinderMock).linkToDeath(any(), anyInt());
|
||||
|
||||
when(mFmRadioModuleMock.openSession(eq(mTunerCallbackMock)))
|
||||
.thenReturn(mFmTunerSessionMock);
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,15 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
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);
|
||||
|
||||
// Mocks
|
||||
@Mock
|
||||
@@ -63,14 +72,7 @@ public final class RadioModuleTest {
|
||||
|
||||
@Before
|
||||
public void setup() throws RemoteException {
|
||||
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,
|
||||
/* supportedProgramTypes= */ new int[]{},
|
||||
/* supportedIdentifierTypes */ new int[]{},
|
||||
/* dabFrequencyTable= */ null, /* vendorInfo= */ null), mLock);
|
||||
mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES, mLock);
|
||||
|
||||
// TODO(b/241118988): test non-null image for getImage method
|
||||
when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(null);
|
||||
@@ -87,6 +89,12 @@ public final class RadioModuleTest {
|
||||
.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 setInternalHalCallback_callbackSetInHal() throws Exception {
|
||||
mRadioModule.setInternalHalCallback();
|
||||
|
||||
@@ -169,7 +169,7 @@ public final class BroadcastRadioServiceImpl {
|
||||
synchronized (mLock) {
|
||||
List<RadioManager.ModuleProperties> moduleList = new ArrayList<>(mModules.size());
|
||||
for (int i = 0; i < mModules.size(); i++) {
|
||||
moduleList.add(mModules.valueAt(i).mProperties);
|
||||
moduleList.add(mModules.valueAt(i).getProperties());
|
||||
}
|
||||
return moduleList;
|
||||
}
|
||||
|
||||
@@ -54,11 +54,11 @@ final class RadioModule {
|
||||
private static final int RADIO_EVENT_LOGGER_QUEUE_SIZE = 25;
|
||||
|
||||
private final IBroadcastRadio mService;
|
||||
public final RadioManager.ModuleProperties mProperties;
|
||||
|
||||
private final Object mLock;
|
||||
private final Handler mHandler;
|
||||
private final RadioLogger mLogger;
|
||||
private final RadioManager.ModuleProperties mProperties;
|
||||
|
||||
/**
|
||||
* Tracks antenna state reported by HAL (if any).
|
||||
@@ -217,6 +217,10 @@ final class RadioModule {
|
||||
return mService;
|
||||
}
|
||||
|
||||
public RadioManager.ModuleProperties getProperties() {
|
||||
return mProperties;
|
||||
}
|
||||
|
||||
void setInternalHalCallback() throws RemoteException {
|
||||
synchronized (mLock) {
|
||||
mService.setTunerCallback(mHalTunerCallback);
|
||||
|
||||
Reference in New Issue
Block a user