/*
* 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 android.car.evs;
import static android.car.evs.CarEvsManager.ERROR_NONE;
import static android.car.evs.CarEvsManager.ERROR_UNAVAILABLE;
import static android.car.feature.Flags.FLAG_CAR_EVS_STREAM_MANAGEMENT;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.car.evs.CarEvsManager.CarEvsStatusListener;
import android.car.evs.CarEvsManager.CarEvsStreamCallback;
import android.car.feature.FakeFeatureFlagsImpl;
import android.hardware.HardwareBuffer;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import com.android.car.internal.ICarBase;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
/**
*
This class contains unit tests for the {@link CarEvsManager}.
*/
@RunWith(MockitoJUnitRunner.class)
public final class CarEvsManagerUnitTest {
@Rule
public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
private static final int[] SERVICE_TYPES = {
CarEvsManager.SERVICE_TYPE_REARVIEW, CarEvsManager.SERVICE_TYPE_SURROUNDVIEW,
CarEvsManager.SERVICE_TYPE_FRONTVIEW, CarEvsManager.SERVICE_TYPE_LEFTVIEW,
CarEvsManager.SERVICE_TYPE_RIGHTVIEW, CarEvsManager.SERVICE_TYPE_DRIVERVIEW,
CarEvsManager.SERVICE_TYPE_FRONT_PASSENGERSVIEW,
CarEvsManager.SERVICE_TYPE_REAR_PASSENGERSVIEW,
};
@Mock private ICarBase mMockCar;
@Mock private CarEvsStatusListener mMockCarEvsStatusListener;
@Mock private CarEvsStreamCallback mMockCarEvsStreamCallback;
@Mock private IBinder mMockBinder;
@Mock private ICarEvsService.Stub mMockICarEvsService;
@Captor private ArgumentCaptor mCarEvsStatusListenerCaptor;
@Captor private ArgumentCaptor mCarEvsStreamCallbackCaptor;
@Captor private ArgumentCaptor mCarEvsStatusCaptor;
private final FakeFeatureFlagsImpl mFakeFeatureFlags = new FakeFeatureFlagsImpl();
private CarEvsManager mManager;
@Before
public void setUp() throws Exception {
mFakeFeatureFlags.setFlag(FLAG_CAR_EVS_STREAM_MANAGEMENT, true);
when(mMockBinder.queryLocalInterface(anyString())).thenReturn(mMockICarEvsService);
mManager = new CarEvsManager(mMockCar, mMockBinder, mFakeFeatureFlags);
assertThat(mManager).isNotNull();
}
@After
public void tearDown() throws Exception {
mManager = null;
}
@Test
public void testSetStatusListenerWithInvalidArguments() {
assertThrows(NullPointerException.class,
() -> mManager.setStatusListener(/* executor= */ null, /* listener= */ null));
assertThrows(NullPointerException.class,
() -> mManager.setStatusListener(/* executor= */ null, mMockCarEvsStatusListener));
}
@Test
public void testSetStatusListenerWithValidArguments() throws Exception {
mManager.setStatusListener(Runnable::run, mMockCarEvsStatusListener);
verify(mMockICarEvsService, atLeastOnce()).registerStatusListener(any());
}
@Test
public void testSetStatusListenerWithValidArgumentsTwice() throws Exception {
mManager.setStatusListener(Runnable::run, mMockCarEvsStatusListener);
verify(mMockICarEvsService, atLeastOnce()).registerStatusListener(any());
IllegalStateException thrown = assertThrows(IllegalStateException.class,
() -> mManager.setStatusListener(Runnable::run, mMockCarEvsStatusListener));
assertWithMessage("Register CarEvsStatusListener")
.that(thrown).hasMessageThat()
.contains("A status listener is already registered.");
}
@Test
public void testSetStatusListenerWithValidArgumentsRemoteExceptionThrown() throws Exception {
doThrow(new RemoteException()).when(mMockICarEvsService).registerStatusListener(any());
mManager.setStatusListener(Runnable::run, mMockCarEvsStatusListener);
verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
}
@Test
public void testStatusChangedEvent() throws Exception {
doNothing().when(mMockICarEvsService)
.registerStatusListener(mCarEvsStatusListenerCaptor.capture());
mManager.setStatusListener(Runnable::run, mMockCarEvsStatusListener);
ICarEvsStatusListener listener = mCarEvsStatusListenerCaptor.getValue();
listener.onStatusChanged(new CarEvsStatus(CarEvsManager.SERVICE_TYPE_REARVIEW,
CarEvsManager.SERVICE_STATE_INACTIVE));
verify(mMockCarEvsStatusListener, atLeastOnce())
.onStatusChanged(mCarEvsStatusCaptor.capture());
CarEvsStatus received = mCarEvsStatusCaptor.getValue();
assertThat(received.describeContents()).isEqualTo(0);
assertThat(received.toString()).contains("CarEvsStatus:");
}
@Test
public void testClearStatusListener() throws Exception {
doNothing().when(mMockICarEvsService)
.registerStatusListener(mCarEvsStatusListenerCaptor.capture());
mManager.setStatusListener(Runnable::run, mMockCarEvsStatusListener);
ICarEvsStatusListener listener = mCarEvsStatusListenerCaptor.getValue();
mManager.clearStatusListener();
verify(mMockICarEvsService, atLeastOnce()).unregisterStatusListener(listener);
}
@Test
public void testClearStatusListenerRemoteExceptionThrown() throws Exception {
doThrow(new RemoteException()).when(mMockICarEvsService).unregisterStatusListener(any());
mManager.clearStatusListener();
verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
}
@Test
@IgnoreUnderRavenwood(blockedBy = HardwareBuffer.class)
public void testStartVideoStreamWithoutToken() throws Exception {
when(mMockICarEvsService
.startVideoStream(anyInt(), any(), mCarEvsStreamCallbackCaptor.capture()))
.thenReturn(ERROR_NONE);
assertThat(mManager.startVideoStream(CarEvsManager.SERVICE_TYPE_REARVIEW,
/* token= */ null, Runnable::run,
mMockCarEvsStreamCallback))
.isEqualTo(ERROR_NONE);
ICarEvsStreamCallback cb = mCarEvsStreamCallbackCaptor.getValue();
cb.onStreamEvent(CarEvsManager.SERVICE_TYPE_REARVIEW,
CarEvsManager.STREAM_EVENT_STREAM_STOPPED);
verify(mMockCarEvsStreamCallback, atLeastOnce()).onStreamEvent(
CarEvsManager.SERVICE_TYPE_REARVIEW, CarEvsManager.STREAM_EVENT_STREAM_STOPPED);
int bufferId = 1;
HardwareBuffer hwbuffer =
HardwareBuffer.create(/* width= */ 64, /* height= */ 32,
/* format= */ HardwareBuffer.RGBA_8888,
/* layers= */ 1,
/* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
CarEvsBufferDescriptor buffer = new CarEvsBufferDescriptor(bufferId,
CarEvsManager.SERVICE_TYPE_REARVIEW, hwbuffer);
cb.onNewFrame(buffer);
verify(mMockCarEvsStreamCallback, atLeastOnce()).onNewFrame(buffer);
}
@Test
public void testStartVideoStreamWithoutTokenRemoteExceptionThrown() throws Exception {
when(mMockICarEvsService.startVideoStream(anyInt(), any(), any()))
.thenThrow(new RemoteException());
assertThat(mManager.startVideoStream(/* type= */ CarEvsManager.SERVICE_TYPE_REARVIEW,
/* token= */ null,
/* executor= */ Runnable::run,
/* callback= */ mMockCarEvsStreamCallback))
.isEqualTo(ERROR_UNAVAILABLE);
verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
}
@Test
public void testStopVideoStream() throws Exception {
mManager.stopVideoStream();
verify(mMockICarEvsService, never()).stopVideoStream(any());
assertThat(mManager.startVideoStream(/* type= */ CarEvsManager.SERVICE_TYPE_REARVIEW,
/* token= */ null,
/* executor= */ Runnable::run,
/* callback= */ mMockCarEvsStreamCallback))
.isEqualTo(ERROR_NONE);
mManager.stopVideoStream();
verify(mMockICarEvsService, atLeastOnce()).stopVideoStream(any());
}
@Test
public void testStopVideoStreamRemoteExceptionThrown() throws Exception {
doThrow(new RemoteException()).when(mMockICarEvsService).stopVideoStream(any());
assertThat(mManager.startVideoStream(/* type= */ CarEvsManager.SERVICE_TYPE_REARVIEW,
/* token= */ null,
/* executor= */ Runnable::run,
/* callback= */ mMockCarEvsStreamCallback))
.isEqualTo(ERROR_NONE);
mManager.stopVideoStream();
verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
}
@Test
public void testGetCurrentStatus() throws Exception {
when(mMockICarEvsService.getCurrentStatus(anyInt()))
.thenReturn(new CarEvsStatus(CarEvsManager.SERVICE_TYPE_REARVIEW,
CarEvsManager.SERVICE_STATE_INACTIVE));
CarEvsStatus currentStatus = mManager.getCurrentStatus();
assertThat(currentStatus).isNotNull();
assertThat(currentStatus.getServiceType()).isEqualTo(CarEvsManager.SERVICE_TYPE_REARVIEW);
assertThat(currentStatus.getState()).isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
}
@Test
public void testGetCurrentStatusWithType() throws Exception {
for (int type : SERVICE_TYPES) {
when(mMockICarEvsService.getCurrentStatus(anyInt()))
.thenReturn(new CarEvsStatus(type, CarEvsManager.SERVICE_STATE_INACTIVE));
CarEvsStatus currentStatus = mManager.getCurrentStatus(type);
assertThat(currentStatus).isNotNull();
assertThat(currentStatus.getServiceType()).isEqualTo(type);
assertThat(currentStatus.getState()).isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
}
}
@Test
public void testGetCurrentStatusRemoteExceptionThrown() throws Exception {
when(mMockICarEvsService.getCurrentStatus(anyInt())).thenThrow(new RemoteException());
CarEvsStatus currentStatus = mManager.getCurrentStatus();
assertThat(currentStatus.getServiceType()).isEqualTo(CarEvsManager.SERVICE_TYPE_REARVIEW);
assertThat(currentStatus.getState()).isEqualTo(CarEvsManager.SERVICE_STATE_UNAVAILABLE);
}
@Test
public void testGetCurrentStatusWithTypeRemoteExceptionThrown() throws Exception {
when(mMockICarEvsService.getCurrentStatus(anyInt())).thenThrow(new RemoteException());
for (int type : SERVICE_TYPES) {
assertThat(mManager.getCurrentStatus(type)).isNull();
}
}
@Test
public void testGenerateSessionToken() throws Exception {
assertThat(mManager.generateSessionToken()).isNotNull();
when(mMockICarEvsService.generateSessionToken()).thenReturn(null);
assertThat(mManager.generateSessionToken()).isNotNull();
}
@Test
public void testGenerateSessionTokenRemoteExceptionThrown() throws Exception {
when(mMockICarEvsService.generateSessionToken()).thenThrow(new RemoteException());
assertThat(mManager.generateSessionToken()).isNotNull();
}
@Test
public void testIsSupported() throws Exception {
when(mMockICarEvsService.isSupported(CarEvsManager.SERVICE_TYPE_REARVIEW)).thenReturn(true);
when(mMockICarEvsService.isSupported(CarEvsManager.SERVICE_TYPE_SURROUNDVIEW))
.thenReturn(false);
assertThat(mManager.isSupported(CarEvsManager.SERVICE_TYPE_REARVIEW)).isTrue();
assertThat(mManager.isSupported(CarEvsManager.SERVICE_TYPE_SURROUNDVIEW)).isFalse();
}
@Test
public void testIsSupportedRemoteExceptionThrown() throws Exception {
when(mMockICarEvsService.isSupported(anyInt())).thenThrow(new RemoteException());
assertThat(mManager.isSupported(CarEvsManager.SERVICE_TYPE_REARVIEW)).isFalse();
}
@Test
@IgnoreUnderRavenwood(blockedBy = HardwareBuffer.class)
public void testReturnFrameBuffer() throws Exception {
int bufferId = 1;
HardwareBuffer hwbuffer =
HardwareBuffer.create(/* width= */ 64, /* height= */ 32,
/* format= */ HardwareBuffer.RGBA_8888,
/* layers= */ 1,
/* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
CarEvsBufferDescriptor buffer = new CarEvsBufferDescriptor(bufferId, hwbuffer);
mManager.returnFrameBuffer(buffer);
verify(mMockICarEvsService, atLeastOnce()).returnFrameBuffer(buffer);
}
@Test
@IgnoreUnderRavenwood(blockedBy = HardwareBuffer.class)
public void testReturnFrameBufferRemoteExceptionThrown() throws Exception {
doThrow(new RemoteException()).when(mMockICarEvsService).returnFrameBuffer(any());
int bufferId = 1;
HardwareBuffer hwbuffer =
HardwareBuffer.create(/* width= */ 64, /* height= */ 32,
/* format= */ HardwareBuffer.RGBA_8888,
/* layers= */ 1,
/* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
CarEvsBufferDescriptor buffer = new CarEvsBufferDescriptor(bufferId, hwbuffer);
mManager.returnFrameBuffer(buffer);
verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
}
@Test
public void testStartActivity() throws Exception {
when(mMockICarEvsService.startActivity(anyInt())).thenReturn(ERROR_NONE);
assertThat(mManager.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW))
.isEqualTo(ERROR_NONE);
verify(mMockICarEvsService, atLeastOnce()).startActivity(anyInt());
}
@Test
public void testStartActivityRemoteExceptionThrown() throws Exception {
when(mMockICarEvsService.startActivity(anyInt())).thenThrow(new RemoteException());
assertThat(mManager.startActivity(CarEvsManager.SERVICE_TYPE_REARVIEW))
.isEqualTo(ERROR_UNAVAILABLE);
verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
}
@Test
public void testStopActivity() throws Exception {
mManager.stopActivity();
verify(mMockICarEvsService, atLeastOnce()).stopActivity();
}
@Test
public void testStopActivityRemoteExceptionThrown() throws Exception {
doThrow(new RemoteException()).when(mMockICarEvsService).stopActivity();
mManager.stopActivity();
verify(mMockCar, atLeastOnce()).handleRemoteExceptionFromCarService(any());
}
@Test
public void testOnCarDisconnected() {
mManager.onCarDisconnected();
}
@Test
public void testCarEvsStatus() {
CarEvsStatus[] arr = CarEvsStatus.CREATOR.newArray(3);
assertThat(arr.length).isEqualTo(3);
CarEvsStatus original = new CarEvsStatus(CarEvsManager.SERVICE_TYPE_REARVIEW,
CarEvsManager.SERVICE_STATE_INACTIVE);
assertThat(original.getServiceType()).isEqualTo(CarEvsManager.SERVICE_TYPE_REARVIEW);
assertThat(original.getState()).isEqualTo(CarEvsManager.SERVICE_STATE_INACTIVE);
android.os.Parcel packet = android.os.Parcel.obtain();
original.writeToParcel(packet, /* flags= */ 0);
assertThat(CarEvsStatus.CREATOR.createFromParcel(packet)).isNotNull();
}
@Test
@IgnoreUnderRavenwood(blockedBy = HardwareBuffer.class)
public void testCarEvsBufferDescriptor() {
CarEvsBufferDescriptor[] arr = CarEvsBufferDescriptor.CREATOR.newArray(3);
assertThat(arr.length).isEqualTo(3);
int bufferId = 1;
HardwareBuffer hwbuffer =
HardwareBuffer.create(/* width= */ 64, /* height= */ 32,
/* format= */ HardwareBuffer.RGBA_8888,
/* layers= */ 1,
/* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN);
CarEvsBufferDescriptor original = new CarEvsBufferDescriptor(bufferId, hwbuffer);
assertThat(original.getId()).isEqualTo(bufferId);
assertThat(original.getHardwareBuffer().getWidth()).isEqualTo(64);
assertThat(original.getHardwareBuffer().getHeight()).isEqualTo(32);
assertThat(original.getHardwareBuffer().getFormat()).isEqualTo(HardwareBuffer.RGBA_8888);
android.os.Parcel packet = android.os.Parcel.obtain();
original.writeToParcel(packet, /* flags= */ 0);
packet.setDataPosition(0);
assertThat(CarEvsBufferDescriptor.CREATOR.createFromParcel(packet)).isNotNull();
}
}