blob: f24e3c747ca14bb8bb870135587cf5a24bd79eb0 [file] [log] [blame]
/*
* Copyright (C) 2020 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.google.android.connecteddevice.ui;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import androidx.lifecycle.Observer;
import android.bluetooth.BluetoothAdapter;
import android.os.RemoteException;
import androidx.arch.core.executor.testing.InstantTaskExecutorRule;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.connecteddevice.api.IAssociatedDeviceManager;
import com.google.android.connecteddevice.api.IAssociationCallback;
import com.google.android.connecteddevice.api.IConnectionCallback;
import com.google.android.connecteddevice.api.IDeviceAssociationCallback;
import com.google.android.connecteddevice.api.IOnAssociatedDevicesRetrievedListener;
import com.google.android.connecteddevice.model.AssociatedDevice;
import com.google.android.connecteddevice.model.AssociatedDeviceDetails;
import com.google.android.connecteddevice.model.ConnectedDevice;
import com.google.android.connecteddevice.ui.AssociatedDeviceViewModel.AssociationState;
import java.util.Collections;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@RunWith(AndroidJUnit4.class)
public final class AssociatedDeviceViewModelTest {
private static final String TEST_ASSOCIATED_DEVICE_ID = "test_device_id";
private static final String TEST_ASSOCIATED_DEVICE_NAME_1 = "test_device_name_1";
private static final String TEST_ASSOCIATED_DEVICE_NAME_2 = "test_device_name_2";
private static final String TEST_ASSOCIATED_DEVICE_ADDRESS = "test_device_address";
private static final String TEST_CAR_NAME = "test_car_name";
private static final String TEST_VERIFICATION_CODE = "test_code";
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
@Rule
public final InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();
@Mock private IAssociatedDeviceManager mockAssociatedDeviceManager;
@Mock private Observer<AssociationState> mockAssociationStateObserver;
@Mock private Observer<AssociatedDeviceDetails> mockDeviceDetailsObserver;
@Mock private Observer<String> mockCarNameObserver;
@Mock private Observer<String> mockPairingCodeObserver;
@Mock private Observer<AssociatedDevice> mockRemovedDeviceObserver;
private AssociatedDeviceViewModel viewModel;
private IAssociationCallback associationCallback;
private IDeviceAssociationCallback deviceAssociationCallback;
private IConnectionCallback connectionCallback;
private IOnAssociatedDevicesRetrievedListener devicesRetrievedListener;
@Before
public void setUp() throws RemoteException {
viewModel =
new AssociatedDeviceViewModel(
ApplicationProvider.getApplicationContext(),
mockAssociatedDeviceManager,
/* isSppEnabled= */ false);
BluetoothAdapter.getDefaultAdapter().enable();
captureCallbacks();
}
@Test
public void acceptVerification() throws RemoteException {
viewModel.acceptVerification();
verify(mockAssociatedDeviceManager).acceptVerification();
}
@Test
public void removeCurrentDevice() throws RemoteException {
AssociatedDevice testDevice = createAssociatedDevice(/* isConnectionEnabled= */ true);
devicesRetrievedListener.onAssociatedDevicesRetrieved(Collections.singletonList(testDevice));
viewModel.removeCurrentDevice();
verify(mockAssociatedDeviceManager).removeAssociatedDevice(eq(TEST_ASSOCIATED_DEVICE_ID));
}
@Test
public void toggleConnectionStatusForCurrentDevice_disableDevice() throws RemoteException {
AssociatedDevice testDevice = createAssociatedDevice(/* isConnectionEnabled= */ true);
devicesRetrievedListener.onAssociatedDevicesRetrieved(Collections.singletonList(testDevice));
viewModel.toggleConnectionStatusForCurrentDevice();
verify(mockAssociatedDeviceManager)
.disableAssociatedDeviceConnection(eq(TEST_ASSOCIATED_DEVICE_ID));
}
@Test
public void toggleConnectionStatusForCurrentDevice_enableDevice() throws RemoteException {
AssociatedDevice testDevice = createAssociatedDevice(/* isConnectionEnabled= */ false);
devicesRetrievedListener.onAssociatedDevicesRetrieved(Collections.singletonList(testDevice));
viewModel.toggleConnectionStatusForCurrentDevice();
verify(mockAssociatedDeviceManager)
.enableAssociatedDeviceConnection(eq(TEST_ASSOCIATED_DEVICE_ID));
}
@Test
public void startAssociation_deviceNotReady() throws RemoteException {
BluetoothAdapter.getDefaultAdapter().disable();
viewModel.startAssociation();
viewModel.getAssociationState().observeForever(mockAssociationStateObserver);
verify(mockAssociationStateObserver).onChanged(AssociationState.PENDING);
verify(mockAssociatedDeviceManager, never()).startAssociation();
}
@Test
public void startAssociation_waitingForPasswordSetup() throws RemoteException {
viewModel.startAssociation();
viewModel.getAssociationState().observeForever(mockAssociationStateObserver);
verify(mockAssociationStateObserver).onChanged(AssociationState.STARTING);
verify(mockAssociatedDeviceManager).startAssociation();
}
@Test
public void startAssociation_inProgress() throws RemoteException {
viewModel.startAssociation();
associationCallback.onAssociationStartSuccess(TEST_CAR_NAME);
viewModel.getAssociationState().observeForever(mockAssociationStateObserver);
viewModel.getAdvertisedCarName().observeForever(mockCarNameObserver);
verify(mockAssociationStateObserver).onChanged(AssociationState.STARTED);
verify(mockCarNameObserver).onChanged(TEST_CAR_NAME);
}
@Test
public void startAssociation_waitingForVerification() throws RemoteException {
viewModel.startAssociation();
associationCallback.onAssociationStartSuccess(TEST_CAR_NAME);
associationCallback.onVerificationCodeAvailable(TEST_VERIFICATION_CODE);
viewModel.getAssociationState().observeForever(mockAssociationStateObserver);
viewModel.getPairingCode().observeForever(mockPairingCodeObserver);
verify(mockAssociationStateObserver).onChanged(AssociationState.STARTED);
verify(mockPairingCodeObserver).onChanged(TEST_VERIFICATION_CODE);
}
@Test
public void startAssociation_associationCompleted() throws RemoteException {
viewModel.startAssociation();
associationCallback.onAssociationStartSuccess(TEST_CAR_NAME);
associationCallback.onVerificationCodeAvailable(TEST_VERIFICATION_CODE);
associationCallback.onAssociationCompleted();
viewModel.getAssociationState().observeForever(mockAssociationStateObserver);
verify(mockAssociationStateObserver).onChanged(AssociationState.COMPLETED);
}
@Test
public void stopAssociation() throws RemoteException {
viewModel.startAssociation();
viewModel.stopAssociation();
viewModel.getAssociationState().observeForever(mockAssociationStateObserver);
verify(mockAssociationStateObserver).onChanged(AssociationState.NONE);
verify(mockAssociatedDeviceManager).stopAssociation();
}
@Test
public void retrieveAssociatedDevice() throws RemoteException {
AssociatedDevice testDevice = createAssociatedDevice(/* isConnectionEnabled= */ true);
devicesRetrievedListener.onAssociatedDevicesRetrieved(Collections.singletonList(testDevice));
viewModel.getCurrentDeviceDetails().observeForever(mockDeviceDetailsObserver);
assertThat(viewModel.getCurrentDeviceDetails().getValue().getAssociatedDevice())
.isEqualTo(testDevice);
}
@Test
public void associatedDeviceUpdated() throws RemoteException {
AssociatedDevice testDevice = createAssociatedDevice(/* isConnectionEnabled= */ true);
devicesRetrievedListener.onAssociatedDevicesRetrieved(Collections.singletonList(testDevice));
AssociatedDevice updatedDevice =
new AssociatedDevice(
TEST_ASSOCIATED_DEVICE_ID,
TEST_ASSOCIATED_DEVICE_ADDRESS,
TEST_ASSOCIATED_DEVICE_NAME_2,
/* isConnectionEnabled= */ true);
deviceAssociationCallback.onAssociatedDeviceUpdated(updatedDevice);
viewModel.getCurrentDeviceDetails().observeForever(mockDeviceDetailsObserver);
assertThat(viewModel.getCurrentDeviceDetails().getValue().getDeviceName())
.isEqualTo(TEST_ASSOCIATED_DEVICE_NAME_2);
}
@Test
public void removeAssociatedDevice() throws RemoteException {
AssociatedDevice testDevice = createAssociatedDevice(/* isConnectionEnabled= */ true);
devicesRetrievedListener.onAssociatedDevicesRetrieved(Collections.singletonList(testDevice));
viewModel.removeCurrentDevice();
verify(mockAssociatedDeviceManager).removeAssociatedDevice(eq(TEST_ASSOCIATED_DEVICE_ID));
}
@Test
public void associatedDeviceRemoved() throws RemoteException {
AssociatedDevice testDevice = createAssociatedDevice(/* isConnectionEnabled= */ true);
devicesRetrievedListener.onAssociatedDevicesRetrieved(Collections.singletonList(testDevice));
deviceAssociationCallback.onAssociatedDeviceRemoved(testDevice);
viewModel.getRemovedDevice().observeForever(mockRemovedDeviceObserver);
verify(mockRemovedDeviceObserver).onChanged(testDevice);
}
@Test
public void associatedDeviceConnected() throws RemoteException {
AssociatedDevice testAssociatedDevice = createAssociatedDevice(/* isConnectionEnabled= */ true);
devicesRetrievedListener.onAssociatedDevicesRetrieved(
Collections.singletonList(testAssociatedDevice));
ConnectedDevice testConnectedDevice = createConnectedDevice();
connectionCallback.onDeviceConnected(testConnectedDevice);
viewModel.getCurrentDeviceDetails().observeForever(mockDeviceDetailsObserver);
assertThat(viewModel.getCurrentDeviceDetails().getValue().isConnected()).isTrue();
}
@Test
public void associatedDeviceDisconnected() throws RemoteException {
AssociatedDevice testAssociatedDevice = createAssociatedDevice(/* isConnectionEnabled= */ true);
devicesRetrievedListener.onAssociatedDevicesRetrieved(
Collections.singletonList(testAssociatedDevice));
ConnectedDevice testConnectedDevice = createConnectedDevice();
connectionCallback.onDeviceConnected(testConnectedDevice);
connectionCallback.onDeviceDisconnected(testConnectedDevice);
viewModel.getCurrentDeviceDetails().observeForever(mockDeviceDetailsObserver);
assertThat(viewModel.getCurrentDeviceDetails().getValue().isConnected()).isFalse();
}
@Test
public void onCleard_doesNotThrowBeforeConnectionEstablished() {
AssociatedDeviceViewModel associatedDeviceViewModel = new AssociatedDeviceViewModel(
ApplicationProvider.getApplicationContext(),
/* associatedDeviceManager= */ null,
/* isSppEnabled= */ false);
associatedDeviceViewModel.onCleared();
}
private void captureCallbacks() throws RemoteException {
ArgumentCaptor<IAssociationCallback> associationCallbackCaptor =
ArgumentCaptor.forClass(IAssociationCallback.class);
verify(mockAssociatedDeviceManager).setAssociationCallback(associationCallbackCaptor.capture());
associationCallback = associationCallbackCaptor.getValue();
ArgumentCaptor<IDeviceAssociationCallback> deviceAssociationCallbackCaptor =
ArgumentCaptor.forClass(IDeviceAssociationCallback.class);
verify(mockAssociatedDeviceManager)
.setDeviceAssociationCallback(deviceAssociationCallbackCaptor.capture());
deviceAssociationCallback = deviceAssociationCallbackCaptor.getValue();
ArgumentCaptor<IConnectionCallback> connectionCallbackCaptor =
ArgumentCaptor.forClass(IConnectionCallback.class);
verify(mockAssociatedDeviceManager).setConnectionCallback(connectionCallbackCaptor.capture());
connectionCallback = connectionCallbackCaptor.getValue();
ArgumentCaptor<IOnAssociatedDevicesRetrievedListener> devicesRetrievedListenerCaptor =
ArgumentCaptor.forClass(IOnAssociatedDevicesRetrievedListener.class);
verify(mockAssociatedDeviceManager)
.retrievedActiveUserAssociatedDevices(devicesRetrievedListenerCaptor.capture());
devicesRetrievedListener = devicesRetrievedListenerCaptor.getValue();
}
private static AssociatedDevice createAssociatedDevice(boolean isConnectionEnabled) {
return new AssociatedDevice(
TEST_ASSOCIATED_DEVICE_ID,
TEST_ASSOCIATED_DEVICE_ADDRESS,
TEST_ASSOCIATED_DEVICE_NAME_1,
isConnectionEnabled);
}
private static ConnectedDevice createConnectedDevice() {
return new ConnectedDevice(
TEST_ASSOCIATED_DEVICE_ID,
TEST_ASSOCIATED_DEVICE_NAME_1,
/* belongsToActiveUser= */ true,
/* hasSecureChannel= */ true);
}
}