package com.mtramin.rxfingerprint; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; import android.hardware.fingerprint.FingerprintManager.CryptoObject; import android.os.CancellationSignal; import android.os.Handler; import com.mtramin.rxfingerprint.data.FingerprintAuthenticationException; import com.mtramin.rxfingerprint.data.FingerprintAuthenticationResult; import com.mtramin.rxfingerprint.data.FingerprintResult; import com.mtramin.rxfingerprint.data.FingerprintUnavailableException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import io.reactivex.Observable; import io.reactivex.observers.TestObserver; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * Tests for the Fingerprint authentication observable */ @SuppressWarnings({"NewApi", "MissingPermission"}) @RunWith(MockitoJUnitRunner.class) public class FingerprintAuthenticationTest { private static final String ERROR_MESSAGE = "Error message"; private static final CharSequence MESSAGE_HELP = "Help message"; @Mock FingerprintApiWrapper fingerprintApiWrapper; @Mock FingerprintManager fingerprintManager; @Mock CancellationSignal cancellationSignal; Observable<FingerprintAuthenticationResult> observable; @Before public void setUp() throws Exception { when(fingerprintApiWrapper.createCancellationSignal()).thenReturn(cancellationSignal); observable = Observable.create(new FingerprintAuthenticationObservable(fingerprintApiWrapper)); } @Test public void testFingerprintNotAvailable() throws Exception { when(fingerprintApiWrapper.isUnavailable()).thenReturn(true); observable.test() .assertNoValues() .assertError(FingerprintUnavailableException.class); } @Test public void testAuthenticationSuccessful() throws Exception { when(fingerprintApiWrapper.isUnavailable()).thenReturn(false); when(fingerprintApiWrapper.getFingerprintManager()).thenReturn(fingerprintManager); AuthenticationResult result = mock(AuthenticationResult.class); TestObserver<FingerprintAuthenticationResult> testObserver = observable.test(); ArgumentCaptor<FingerprintManager.AuthenticationCallback> callbackCaptor = ArgumentCaptor.forClass(FingerprintManager.AuthenticationCallback.class); verify(fingerprintManager).authenticate(any(CryptoObject.class), any(CancellationSignal.class), anyInt(), callbackCaptor.capture(), any(Handler.class)); callbackCaptor.getValue().onAuthenticationSucceeded(result); testObserver.awaitTerminalEvent(); testObserver.assertNoErrors(); testObserver.assertComplete(); testObserver.assertValueCount(1); FingerprintAuthenticationResult fingerprintAuthenticationResult = testObserver.values().get(0); assertTrue("Authentication should be successful", fingerprintAuthenticationResult.isSuccess()); assertTrue("Result should be equal AUTHENTICATED", fingerprintAuthenticationResult.getResult().equals(FingerprintResult.AUTHENTICATED)); assertTrue("Should contain no message", fingerprintAuthenticationResult.getMessage() == null); } @Test public void testAuthenticationError() throws Exception { when(fingerprintApiWrapper.isUnavailable()).thenReturn(false); when(fingerprintApiWrapper.getFingerprintManager()).thenReturn(fingerprintManager); TestObserver<FingerprintAuthenticationResult> testObserver = observable.test(); ArgumentCaptor<FingerprintManager.AuthenticationCallback> callbackCaptor = ArgumentCaptor.forClass(FingerprintManager.AuthenticationCallback.class); verify(fingerprintManager).authenticate(any(CryptoObject.class), any(CancellationSignal.class), anyInt(), callbackCaptor.capture(), any(Handler.class)); callbackCaptor.getValue().onAuthenticationError(0, ERROR_MESSAGE); testObserver.awaitTerminalEvent(); testObserver.assertError(FingerprintAuthenticationException.class); testObserver.assertValueCount(0); assertTrue("Should contain 1 error", testObserver.errorCount() == 1); Throwable throwable = testObserver.errors().get(0); assertTrue("Message should equal ERROR_MESSAGE", throwable.getMessage().equals(ERROR_MESSAGE)); } @Test public void testAuthenticationFailed() throws Exception { when(fingerprintApiWrapper.isUnavailable()).thenReturn(false); when(fingerprintApiWrapper.getFingerprintManager()).thenReturn(fingerprintManager); TestObserver<FingerprintAuthenticationResult> testObserver = observable.test(); ArgumentCaptor<FingerprintManager.AuthenticationCallback> callbackCaptor = ArgumentCaptor.forClass(FingerprintManager.AuthenticationCallback.class); verify(fingerprintManager).authenticate(any(CryptoObject.class), any(CancellationSignal.class), anyInt(), callbackCaptor.capture(), any(Handler.class)); callbackCaptor.getValue().onAuthenticationFailed(); testObserver.assertNotTerminated(); testObserver.assertNoErrors(); testObserver.assertNotComplete(); testObserver.assertValueCount(1); FingerprintAuthenticationResult fingerprintAuthenticationResult = testObserver.values().get(0); assertTrue("Authentication should not be successful", !fingerprintAuthenticationResult.isSuccess()); assertTrue("Result should be equal FAILED", fingerprintAuthenticationResult.getResult().equals(FingerprintResult.FAILED)); assertTrue("Should contain no message", fingerprintAuthenticationResult.getMessage() == null); } @Test public void testAuthenticationHelp() throws Exception { when(fingerprintApiWrapper.isUnavailable()).thenReturn(false); when(fingerprintApiWrapper.getFingerprintManager()).thenReturn(fingerprintManager); TestObserver<FingerprintAuthenticationResult> testObserver = observable.test(); ArgumentCaptor<FingerprintManager.AuthenticationCallback> callbackCaptor = ArgumentCaptor.forClass(FingerprintManager.AuthenticationCallback.class); verify(fingerprintManager).authenticate(any(CryptoObject.class), any(CancellationSignal.class), anyInt(), callbackCaptor.capture(), any(Handler.class)); callbackCaptor.getValue().onAuthenticationHelp(0, MESSAGE_HELP); testObserver.assertNotTerminated(); testObserver.assertNoErrors(); testObserver.assertNotComplete(); testObserver.assertValueCount(1); FingerprintAuthenticationResult fingerprintAuthenticationResult = testObserver.values().get(0); assertTrue("Authentication should not be successful", !fingerprintAuthenticationResult.isSuccess()); assertTrue("Result should be equal HELP", fingerprintAuthenticationResult.getResult().equals(FingerprintResult.HELP)); assertTrue("Should contain help message", fingerprintAuthenticationResult.getMessage().equals(MESSAGE_HELP)); } @Test public void testAuthenticationSuccessfulOnSecondTry() throws Exception { when(fingerprintApiWrapper.isUnavailable()).thenReturn(false); when(fingerprintApiWrapper.getFingerprintManager()).thenReturn(fingerprintManager); TestObserver<FingerprintAuthenticationResult> testObserver = observable.test(); ArgumentCaptor<FingerprintManager.AuthenticationCallback> callbackCaptor = ArgumentCaptor.forClass(FingerprintManager.AuthenticationCallback.class); verify(fingerprintManager).authenticate(any(CryptoObject.class), any(CancellationSignal.class), anyInt(), callbackCaptor.capture(), any(Handler.class)); callbackCaptor.getValue().onAuthenticationHelp(0, MESSAGE_HELP); testObserver.assertNotTerminated(); testObserver.assertNoErrors(); testObserver.assertNotComplete(); testObserver.assertValueCount(1); FingerprintAuthenticationResult helpResult = testObserver.values().get(0); assertTrue("Authentication should not be successful", !helpResult.isSuccess()); assertTrue("Result should be equal HELP", helpResult.getResult().equals(FingerprintResult.HELP)); assertTrue("Should contain help message", helpResult.getMessage().equals(MESSAGE_HELP)); callbackCaptor.getValue().onAuthenticationSucceeded(mock(AuthenticationResult.class)); testObserver.awaitTerminalEvent(); testObserver.assertNoErrors(); testObserver.assertComplete(); testObserver.assertValueCount(2); FingerprintAuthenticationResult successResult = testObserver.values().get(1); assertTrue("Authentication should be successful", successResult.isSuccess()); assertTrue("Result should be equal AUTHENTICATED", successResult.getResult().equals(FingerprintResult.AUTHENTICATED)); assertTrue("Should contain no message", successResult.getMessage() == null); } @Test public void cancelsFingerprintOperationWhenDisposed() throws Exception { when(fingerprintApiWrapper.isUnavailable()).thenReturn(false); when(fingerprintApiWrapper.getFingerprintManager()).thenReturn(fingerprintManager); TestObserver<FingerprintAuthenticationResult> test = observable.test(); test.dispose(); verify(cancellationSignal).cancel(); } }