blob: 4cad535a3a519e75bfbd98f921eb3e0488738713 [file] [log] [blame]
/*
* Copyright 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 android.uwb;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.os.RemoteException;
import android.uwb.UwbManager.AdapterStateCallback;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Test of {@link AdapterStateListener}.
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AdapterStateListenerTest {
IUwbAdapter mUwbAdapter = mock(IUwbAdapter.class);
Answer mRegisterSuccessAnswer = new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0];
try {
cb.onAdapterStateChanged(AdapterState.STATE_DISABLED, StateChangeReason.UNKNOWN);
} catch (RemoteException e) {
// Nothing to do
}
return new Object();
}
};
Throwable mThrowRemoteException = new RemoteException("RemoteException");
private static Executor getExecutor() {
return new Executor() {
@Override
public void execute(Runnable command) {
command.run();
}
};
}
private static void verifyCallbackStateChangedInvoked(
AdapterStateCallback callback, int numTimes) {
verify(callback, times(numTimes)).onStateChanged(anyInt(), anyInt());
}
@Test
public void testRegister_RegisterUnregister() throws RemoteException {
doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback1 = mock(AdapterStateCallback.class);
AdapterStateCallback callback2 = mock(AdapterStateCallback.class);
// Verify that the adapter state listener registered with the UWB Adapter
adapterStateListener.register(getExecutor(), callback1);
verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
verifyCallbackStateChangedInvoked(callback1, 1);
verifyCallbackStateChangedInvoked(callback2, 0);
// Register a second client and no new call to UWB Adapter
adapterStateListener.register(getExecutor(), callback2);
verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
verifyCallbackStateChangedInvoked(callback1, 1);
verifyCallbackStateChangedInvoked(callback2, 1);
// Unregister first callback
adapterStateListener.unregister(callback1);
verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
verify(mUwbAdapter, times(0)).unregisterAdapterStateCallbacks(any());
verifyCallbackStateChangedInvoked(callback1, 1);
verifyCallbackStateChangedInvoked(callback2, 1);
// Unregister second callback
adapterStateListener.unregister(callback2);
verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
verify(mUwbAdapter, times(1)).unregisterAdapterStateCallbacks(any());
verifyCallbackStateChangedInvoked(callback1, 1);
verifyCallbackStateChangedInvoked(callback2, 1);
}
@Test
public void testRegister_RegisterSameCallbackTwice() throws RemoteException {
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback = mock(AdapterStateCallback.class);
doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
adapterStateListener.register(getExecutor(), callback);
verifyCallbackStateChangedInvoked(callback, 1);
adapterStateListener.register(getExecutor(), callback);
verifyCallbackStateChangedInvoked(callback, 1);
// Invoke a state change and ensure the callback is only called once
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
StateChangeReason.UNKNOWN);
verifyCallbackStateChangedInvoked(callback, 2);
}
@Test
public void testCallback_RunViaExecutor_Success() throws RemoteException {
// Verify that the callbacks are invoked on the executor when successful
doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
runViaExecutor();
}
private void runViaExecutor() {
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback = mock(AdapterStateCallback.class);
Executor executor = mock(Executor.class);
// Do not run commands received and ensure that the callback is not invoked
doAnswer(new ExecutorAnswer(false)).when(executor).execute(any());
adapterStateListener.register(executor, callback);
verify(executor, times(1)).execute(any());
verifyCallbackStateChangedInvoked(callback, 0);
// Manually invoke the callback and ensure callback is not invoked
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
StateChangeReason.UNKNOWN);
verify(executor, times(2)).execute(any());
verifyCallbackStateChangedInvoked(callback, 0);
// Run the command that the executor receives
doAnswer(new ExecutorAnswer(true)).when(executor).execute(any());
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
StateChangeReason.UNKNOWN);
verify(executor, times(3)).execute(any());
verifyCallbackStateChangedInvoked(callback, 1);
}
class ExecutorAnswer implements Answer {
final boolean mShouldRun;
ExecutorAnswer(boolean shouldRun) {
mShouldRun = shouldRun;
}
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
if (mShouldRun) {
((Runnable) invocation.getArgument(0)).run();
}
return null;
}
}
@Test
public void testNotify_AllCallbacksNotified() throws RemoteException {
doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
List<AdapterStateCallback> callbacks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
AdapterStateCallback callback = mock(AdapterStateCallback.class);
adapterStateListener.register(getExecutor(), callback);
callbacks.add(callback);
}
// Ensure every callback got the initial state
for (AdapterStateCallback callback : callbacks) {
verifyCallbackStateChangedInvoked(callback, 1);
}
// Invoke a state change and ensure all callbacks are invoked
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
StateChangeReason.ALL_SESSIONS_CLOSED);
for (AdapterStateCallback callback : callbacks) {
verifyCallbackStateChangedInvoked(callback, 2);
}
}
@Test
public void testStateChange_CorrectValue() {
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback = mock(AdapterStateCallback.class);
adapterStateListener.register(getExecutor(), callback);
runStateChangeValue(StateChangeReason.ALL_SESSIONS_CLOSED,
AdapterState.STATE_ENABLED_INACTIVE,
AdapterStateCallback.STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED,
AdapterStateCallback.STATE_ENABLED_INACTIVE);
runStateChangeValue(StateChangeReason.SESSION_STARTED, AdapterState.STATE_ENABLED_ACTIVE,
AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED,
AdapterStateCallback.STATE_ENABLED_ACTIVE);
runStateChangeValue(StateChangeReason.SYSTEM_BOOT, AdapterState.STATE_DISABLED,
AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT,
AdapterStateCallback.STATE_DISABLED);
runStateChangeValue(StateChangeReason.SYSTEM_POLICY, AdapterState.STATE_DISABLED,
AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY,
AdapterStateCallback.STATE_DISABLED);
runStateChangeValue(StateChangeReason.UNKNOWN, AdapterState.STATE_DISABLED,
AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN,
AdapterStateCallback.STATE_DISABLED);
}
private void runStateChangeValue(@StateChangeReason int reasonIn, @AdapterState int stateIn,
@AdapterStateCallback.StateChangedReason int reasonOut,
@AdapterStateCallback.State int stateOut) {
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback = mock(AdapterStateCallback.class);
adapterStateListener.register(getExecutor(), callback);
adapterStateListener.onAdapterStateChanged(stateIn, reasonIn);
verify(callback, times(1)).onStateChanged(stateOut, reasonOut);
}
@Test
public void testStateChange_FirstRegisterGetsCorrectState() throws RemoteException {
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback = mock(AdapterStateCallback.class);
Answer registerAnswer = new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0];
try {
cb.onAdapterStateChanged(AdapterState.STATE_ENABLED_ACTIVE,
StateChangeReason.SESSION_STARTED);
} catch (RemoteException e) {
// Nothing to do
}
return new Object();
}
};
doAnswer(registerAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
adapterStateListener.register(getExecutor(), callback);
verify(callback).onStateChanged(AdapterStateCallback.STATE_ENABLED_ACTIVE,
AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED);
}
@Test
public void testStateChange_SecondRegisterGetsCorrectState() {
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback1 = mock(AdapterStateCallback.class);
AdapterStateCallback callback2 = mock(AdapterStateCallback.class);
adapterStateListener.register(getExecutor(), callback1);
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_ENABLED_ACTIVE,
StateChangeReason.SYSTEM_BOOT);
adapterStateListener.register(getExecutor(), callback2);
verify(callback2).onStateChanged(AdapterStateCallback.STATE_ENABLED_ACTIVE,
AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT);
}
}