/** * Copyright (c) 2018 Uber Technologies, Inc. * * 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.uber.rxcentralble.core; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.content.Context; import android.os.Build; import com.uber.rxcentralble.ConnectionError; import com.uber.rxcentralble.PeripheralError; import com.uber.rxcentralble.Peripheral; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.util.ReflectionHelpers; import java.util.UUID; import io.reactivex.observers.TestObserver; import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_NOTIFY; import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE; import static com.uber.rxcentralble.ConnectionError.Code.CONNECT_FAILED; import static com.uber.rxcentralble.ConnectionError.Code.DISCONNECTION; import static com.uber.rxcentralble.PeripheralError.Code.CHARACTERISTIC_SET_VALUE_FAILED; import static com.uber.rxcentralble.PeripheralError.Code.DISCONNECTED; import static com.uber.rxcentralble.PeripheralError.Code.MISSING_CHARACTERISTIC; import static com.uber.rxcentralble.PeripheralError.Code.OPERATION_IN_PROGRESS; import static com.uber.rxcentralble.PeripheralError.Code.READ_CHARACTERISTIC_FAILED; import static com.uber.rxcentralble.PeripheralError.Code.READ_RSSI_FAILED; import static com.uber.rxcentralble.PeripheralError.Code.REGISTER_NOTIFICATION_FAILED; import static com.uber.rxcentralble.PeripheralError.Code.SERVICE_DISCOVERY_FAILED; import static com.uber.rxcentralble.PeripheralError.Code.SET_CHARACTERISTIC_NOTIFICATION_CCCD_MISSING; import static com.uber.rxcentralble.PeripheralError.Code.SET_CHARACTERISTIC_NOTIFICATION_MISSING_PROPERTY; import static com.uber.rxcentralble.PeripheralError.Code.REQUEST_MTU_FAILED; import static com.uber.rxcentralble.PeripheralError.Code.UNREGISTER_NOTIFICATION_FAILED; import static com.uber.rxcentralble.PeripheralError.Code.WRITE_CHARACTERISTIC_FAILED; import static com.uber.rxcentralble.PeripheralError.Code.WRITE_DESCRIPTOR_FAILED; import static com.uber.rxcentralble.PeripheralError.ERROR_STATUS_CALL_FAILED; import static com.uber.rxcentralble.Peripheral.ConnectableState.CONNECTED; import static com.uber.rxcentralble.Peripheral.ConnectableState.CONNECTING; import static com.uber.rxcentralble.Peripheral.MTU_OVERHEAD; import static com.uber.rxcentralble.core.CorePeripheral.CCCD_UUID; import static com.uber.rxcentralble.core.CorePeripheral.DEFAULT_MTU; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @RunWith(RobolectricTestRunner.class) public class CorePeripheralTest { @Mock BluetoothDevice bluetoothDevice; @Mock Context context; @Mock BluetoothGatt bluetoothGatt; @Mock BluetoothGattService bluetoothGattService; @Mock BluetoothGattCharacteristic bluetoothGattCharacteristic; @Mock BluetoothGattDescriptor bluetoothGattDescriptor; private final UUID svcUuid = UUID.randomUUID(); private final UUID chrUuid = UUID.randomUUID(); private CorePeripheral corePeripheral; private BluetoothGattCallback bluetoothGattCallback; private TestObserver<Peripheral.ConnectableState> connectTestObserver; private TestObserver<byte[]> readTestObserver; private TestObserver<Void> writeTestObserver; private TestObserver<Void> registerNotificationTestObserver; private TestObserver<byte[]> notificationTestObserver; private TestObserver<Integer> setMtuTestObserver; private TestObserver<Integer> readRssiTestObserver; @Before public void setup() { MockitoAnnotations.initMocks(this); ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 21); corePeripheral = new CorePeripheral(bluetoothDevice, context); } @Test public void connect_connectGattFailed() { when(bluetoothDevice.connectGatt(any(), anyBoolean(), any())).thenReturn(null); connectTestObserver = corePeripheral.connect().test(); connectTestObserver.assertError( throwable -> { ConnectionError error = (ConnectionError) throwable; if (error != null && error.getCode() == CONNECT_FAILED && error.getCause() != null && error.getCause() instanceof PeripheralError) { PeripheralError peripheralError = (PeripheralError) error.getCause(); return peripheralError.getErrorStatus() == ERROR_STATUS_CALL_FAILED; } return false; }); } @Test public void connect_gattCallback_nonZeroStatus() { prepareConnect(false); bluetoothGattCallback.onConnectionStateChange(bluetoothGatt, 99, BluetoothGatt.STATE_CONNECTED); connectTestObserver.assertError( throwable -> { ConnectionError error = (ConnectionError) throwable; if (error != null && error.getCode() == CONNECT_FAILED && error.getCause() != null && error.getCause() instanceof PeripheralError) { PeripheralError peripheralError = (PeripheralError) error.getCause(); return peripheralError.getErrorStatus() == 99; } return false; }); verify(bluetoothGatt).disconnect(); } @Test public void connect_serviceDiscoveryGattFailed() { prepareConnect(false); bluetoothGattCallback.onConnectionStateChange(bluetoothGatt, 0, BluetoothGatt.STATE_CONNECTED); connectTestObserver.assertError( throwable -> { ConnectionError error = (ConnectionError) throwable; if (error != null && error.getCode() == CONNECT_FAILED && error.getCause() != null && error.getCause() instanceof PeripheralError) { PeripheralError peripheralError = (PeripheralError) error.getCause(); return peripheralError.getCode() == SERVICE_DISCOVERY_FAILED && peripheralError.getErrorStatus() == ERROR_STATUS_CALL_FAILED; } return false; }); verify(bluetoothGatt).disconnect(); } @Test public void connect_serviceDiscovery_gattCallback_nonZeroStatus() { prepareConnect(true); bluetoothGattCallback.onConnectionStateChange(bluetoothGatt, 0, BluetoothGatt.STATE_CONNECTED); verify(bluetoothGatt).discoverServices(); bluetoothGattCallback.onServicesDiscovered(bluetoothGatt, 99); connectTestObserver.assertError( throwable -> { ConnectionError error = (ConnectionError) throwable; if (error != null && error.getCode() == CONNECT_FAILED && error.getCause() != null && error.getCause() instanceof PeripheralError) { PeripheralError peripheralError = (PeripheralError) error.getCause(); return peripheralError.getCode() == SERVICE_DISCOVERY_FAILED && peripheralError.getErrorStatus() == 99; } return false; }); verify(bluetoothGatt).disconnect(); } @Test public void connect_success() { connect(); connectTestObserver.assertValues(CONNECTING, CONNECTED); } @Test public void connect_verifyMulticasted() { connect(); corePeripheral.connect().test(); verifyNoMoreInteractions(bluetoothGatt); } @Test public void read_disconnected() { readTestObserver = corePeripheral.read(svcUuid, chrUuid).test(); readTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == DISCONNECTED; }); } @Test public void read_characteristicMissing() { connect(); readTestObserver = corePeripheral.read(svcUuid, chrUuid).test(); readTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == MISSING_CHARACTERISTIC; }); } @Test public void read_gattReadFailed() { connect(); prepareRead(false); readTestObserver = corePeripheral.read(svcUuid, chrUuid).test(); readTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == READ_CHARACTERISTIC_FAILED && error.getErrorStatus() == ERROR_STATUS_CALL_FAILED; }); } @Test public void read_gattCallback_nonZeroStatus() { connect(); prepareRead(true); readTestObserver = corePeripheral.read(svcUuid, chrUuid).test(); bluetoothGattCallback.onCharacteristicRead(bluetoothGatt, bluetoothGattCharacteristic, 99); readTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == READ_CHARACTERISTIC_FAILED && error.getErrorStatus() == 99; }); } @Test public void read_success() { connect(); prepareRead(true); byte[] readBytes = new byte[] {0x00}; when(bluetoothGattCharacteristic.getValue()).thenReturn(readBytes); readTestObserver = corePeripheral.read(svcUuid, chrUuid).test(); bluetoothGattCallback.onCharacteristicRead(bluetoothGatt, bluetoothGattCharacteristic, 0); readTestObserver.assertValue(readBytes); } @Test public void read_operationInProgress() { connect(); prepareRead(true); corePeripheral.read(svcUuid, chrUuid).test(); readTestObserver = corePeripheral.read(svcUuid, chrUuid).test(); readTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == OPERATION_IN_PROGRESS; }); } @Test public void write_disconnected() { byte[] writeBytes = new byte[] {0x00}; writeTestObserver = corePeripheral.write(svcUuid, chrUuid, writeBytes).test(); writeTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == DISCONNECTED; }); } @Test public void write_characteristicMissing() { connect(); byte[] writeBytes = new byte[] {0x00}; writeTestObserver = corePeripheral.write(svcUuid, chrUuid, writeBytes).test(); writeTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == MISSING_CHARACTERISTIC; }); } @Test public void write_setValueFailed() { connect(); prepareWrite(false, false); byte[] writeBytes = new byte[] {0x00}; writeTestObserver = corePeripheral.write(svcUuid, chrUuid, writeBytes).test(); writeTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == CHARACTERISTIC_SET_VALUE_FAILED; }); } @Test public void write_gattWriteFailed() { connect(); prepareWrite(true, false); byte[] writeBytes = new byte[] {0x00}; writeTestObserver = corePeripheral.write(svcUuid, chrUuid, writeBytes).test(); writeTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == WRITE_CHARACTERISTIC_FAILED && error.getErrorStatus() == ERROR_STATUS_CALL_FAILED; }); } @Test public void write_gattCallback_nonZeroStatus() { connect(); prepareWrite(true, true); byte[] writeBytes = new byte[] {0x00}; writeTestObserver = corePeripheral.write(svcUuid, chrUuid, writeBytes).test(); bluetoothGattCallback.onCharacteristicWrite(bluetoothGatt, bluetoothGattCharacteristic, 99); writeTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == WRITE_CHARACTERISTIC_FAILED && error.getErrorStatus() == 99; }); } @Test public void write_success() { connect(); prepareWrite(true, true); byte[] writeBytes = new byte[] {0x00}; writeTestObserver = corePeripheral.write(svcUuid, chrUuid, writeBytes).test(); bluetoothGattCallback.onCharacteristicWrite(bluetoothGatt, bluetoothGattCharacteristic, 0); writeTestObserver.assertComplete(); } @Test public void write_operationInProgress() { connect(); prepareWrite(true, true); byte[] writeBytes = new byte[] {0x00}; corePeripheral.write(svcUuid, chrUuid, writeBytes).test(); writeTestObserver = corePeripheral.write(svcUuid, chrUuid, writeBytes).test(); writeTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == OPERATION_IN_PROGRESS; }); } @Test public void registerNotification_disconnected() { registerNotificationTestObserver = corePeripheral.registerNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == DISCONNECTED; }); } @Test public void registerNotification_operationInProgress() { connect(); prepareNotifications(true, true, true, true, true); corePeripheral.registerNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver = corePeripheral.registerNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == OPERATION_IN_PROGRESS; }); } @Test public void registerNotification_characteristicMissing() { connect(); registerNotificationTestObserver = corePeripheral.registerNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == MISSING_CHARACTERISTIC; }); } @Test public void registerNotification_characteristic_missingNotificationProperty() { connect(); prepareNotifications(false, true, false, true, false); registerNotificationTestObserver = corePeripheral.registerNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; if (error != null && error.getCode() == REGISTER_NOTIFICATION_FAILED && error.getCause() != null && error.getCause() instanceof PeripheralError) { PeripheralError peripheralError = (PeripheralError) error.getCause(); return peripheralError.getCode() == SET_CHARACTERISTIC_NOTIFICATION_MISSING_PROPERTY; } return false; }); } @Test public void registerNotification_descriptor_missing() { connect(); prepareNotifications(true, false, false, false, false); registerNotificationTestObserver = corePeripheral.registerNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; if (error != null && error.getCode() == REGISTER_NOTIFICATION_FAILED && error.getCause() != null && error.getCause() instanceof PeripheralError) { PeripheralError peripheralError = (PeripheralError) error.getCause(); return peripheralError.getCode() == SET_CHARACTERISTIC_NOTIFICATION_CCCD_MISSING; } return false; }); } @Test public void registerNotification_setValueFailed() { connect(); prepareNotifications(true, true, false, true, false); registerNotificationTestObserver = corePeripheral.registerNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; if (error != null && error.getCode() == REGISTER_NOTIFICATION_FAILED && error.getCause() != null && error.getCause() instanceof PeripheralError) { PeripheralError peripheralError = (PeripheralError) error.getCause(); return peripheralError.getCode() == CHARACTERISTIC_SET_VALUE_FAILED; } return false; }); } @Test public void registerNotification_descriptor_writeFailed() { connect(); prepareNotifications(true, true, true, true, false); registerNotificationTestObserver = corePeripheral.registerNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; if (error != null && error.getCode() == REGISTER_NOTIFICATION_FAILED && error.getCause() != null && error.getCause() instanceof PeripheralError) { PeripheralError peripheralError = (PeripheralError) error.getCause(); return peripheralError.getCode() == WRITE_DESCRIPTOR_FAILED; } return false; }); } @Test public void registerNotification_gattCallback_nonZeroStatus() { connect(); prepareNotifications(true, true, true, true, true); registerNotificationTestObserver = corePeripheral.registerNotification(svcUuid, chrUuid).test(); bluetoothGattCallback.onDescriptorWrite(bluetoothGatt, bluetoothGattDescriptor, 99); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == WRITE_DESCRIPTOR_FAILED && error.getErrorStatus() == 99; }); } @Test public void registerNotification_success() { connect(); prepareNotifications(true, true, true, true, true); registerNotificationTestObserver = corePeripheral.registerNotification(svcUuid, chrUuid).test(); bluetoothGattCallback.onDescriptorWrite(bluetoothGatt, bluetoothGattDescriptor, 0); registerNotificationTestObserver.assertComplete(); } @Test public void unregisterNotification_disconnected() { registerNotificationTestObserver = corePeripheral.unregisterNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == DISCONNECTED; }); } @Test public void unregisterNotification_operationInProgress() { connect(); prepareNotifications(true, true, true, true, true); corePeripheral.registerNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver = corePeripheral.unregisterNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == OPERATION_IN_PROGRESS; }); } @Test public void unregisterNotification_characteristicMissing() { connect(); registerNotificationTestObserver = corePeripheral.unregisterNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == MISSING_CHARACTERISTIC; }); } @Test public void unregisterNotification_descriptor_missing() { connect(); prepareNotifications(true, false, false, false, false); registerNotificationTestObserver = corePeripheral.unregisterNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; if (error != null && error.getCode() == UNREGISTER_NOTIFICATION_FAILED && error.getCause() != null && error.getCause() instanceof PeripheralError) { PeripheralError peripheralError = (PeripheralError) error.getCause(); return peripheralError.getCode() == SET_CHARACTERISTIC_NOTIFICATION_CCCD_MISSING; } return false; }); } @Test public void unregisterNotification_descriptor_writeFailed() { connect(); prepareNotifications(true, true, true, true, false); registerNotificationTestObserver = corePeripheral.unregisterNotification(svcUuid, chrUuid).test(); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; if (error != null && error.getCode() == UNREGISTER_NOTIFICATION_FAILED && error.getCause() != null && error.getCause() instanceof PeripheralError) { PeripheralError peripheralError = (PeripheralError) error.getCause(); return peripheralError.getCode() == WRITE_DESCRIPTOR_FAILED; } return false; }); } @Test public void unregisterNotification_gattCallback_nonZeroStatus() { connect(); prepareNotifications(true, true, true, true, true); registerNotificationTestObserver = corePeripheral.unregisterNotification(svcUuid, chrUuid).test(); bluetoothGattCallback.onDescriptorWrite(bluetoothGatt, bluetoothGattDescriptor, 99); registerNotificationTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == WRITE_DESCRIPTOR_FAILED && error.getErrorStatus() == 99; }); } @Test public void unregisterNotification_success() { connect(); prepareNotifications(true, true, true, true, true); registerNotificationTestObserver = corePeripheral.unregisterNotification(svcUuid, chrUuid).test(); bluetoothGattCallback.onDescriptorWrite(bluetoothGatt, bluetoothGattDescriptor, 0); registerNotificationTestObserver.assertComplete(); } @Test public void notifications() { connect(); prepareGatt(); byte[] notification = new byte[] {0x00}; when(bluetoothGattCharacteristic.getValue()).thenReturn(notification); notificationTestObserver = corePeripheral.notification(chrUuid).test(); bluetoothGattCallback.onCharacteristicChanged(bluetoothGatt, bluetoothGattCharacteristic); notificationTestObserver.assertValue(notification); } @Test public void setMtu_disconnected() { setMtuTestObserver = corePeripheral.requestMtu(100).test(); setMtuTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == DISCONNECTED; }); } @Test public void setMtu_operationInProgress() { connect(); when(bluetoothGatt.requestMtu(anyInt())).thenReturn(true); corePeripheral.requestMtu(100).test(); setMtuTestObserver = corePeripheral.requestMtu(100).test(); setMtuTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == OPERATION_IN_PROGRESS; }); } @Test public void setMtu_gattFailed() { connect(); when(bluetoothGatt.requestMtu(anyInt())).thenReturn(false); setMtuTestObserver = corePeripheral.requestMtu(100).test(); setMtuTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == REQUEST_MTU_FAILED && error.getErrorStatus() == ERROR_STATUS_CALL_FAILED; }); } @Test public void setMtu_gattCallback_nonZeroStatus() { connect(); when(bluetoothGatt.requestMtu(anyInt())).thenReturn(true); setMtuTestObserver = corePeripheral.requestMtu(100).test(); bluetoothGattCallback.onMtuChanged(bluetoothGatt, -1, 99); setMtuTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == REQUEST_MTU_FAILED && error.getErrorStatus() == 99; }); assertEquals(corePeripheral.getMaxWriteLength(), DEFAULT_MTU - MTU_OVERHEAD); } @Test public void setMtu_success() { connect(); when(bluetoothGatt.requestMtu(anyInt())).thenReturn(true); setMtuTestObserver = corePeripheral.requestMtu(100).test(); bluetoothGattCallback.onMtuChanged(bluetoothGatt, 100, 0); setMtuTestObserver.assertValue(100); assertEquals(corePeripheral.getMaxWriteLength(), 100 - MTU_OVERHEAD); } @Test public void readRssi_disconnected() { readRssiTestObserver = corePeripheral.readRssi().test(); readRssiTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == DISCONNECTED; }); } @Test public void readRssi_operationInProgress() { connect(); when(bluetoothGatt.readRemoteRssi()).thenReturn(true); corePeripheral.readRssi().test(); readRssiTestObserver = corePeripheral.readRssi().test(); readRssiTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == OPERATION_IN_PROGRESS; }); } @Test public void readRssi_gattFailed() { connect(); when(bluetoothGatt.readRemoteRssi()).thenReturn(false); readRssiTestObserver = corePeripheral.readRssi().test(); readRssiTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == READ_RSSI_FAILED && error.getErrorStatus() == ERROR_STATUS_CALL_FAILED; }); } @Test public void readRssi_gattCallback_nonZeroStatus() { connect(); when(bluetoothGatt.readRemoteRssi()).thenReturn(true); readRssiTestObserver = corePeripheral.readRssi().test(); bluetoothGattCallback.onReadRemoteRssi(bluetoothGatt, 0, 99); readRssiTestObserver.assertError( throwable -> { PeripheralError error = (PeripheralError) throwable; return error != null && error.getCode() == READ_RSSI_FAILED && error.getErrorStatus() == 99; }); } @Test public void readRssi_success() { connect(); when(bluetoothGatt.readRemoteRssi()).thenReturn(true); readRssiTestObserver = corePeripheral.readRssi().test(); bluetoothGattCallback.onReadRemoteRssi(bluetoothGatt, 100, 0); readRssiTestObserver.assertValue(100); } @Test public void disconnect() { connect(); corePeripheral.disconnect(); connectTestObserver.assertError( throwable -> { ConnectionError error = (ConnectionError) throwable; return error != null && error.getCode() == DISCONNECTION; }); } private void prepareConnect(boolean discoverServiceSuccess) { when(bluetoothDevice.connectGatt(any(), anyBoolean(), any())).thenReturn(bluetoothGatt); when(bluetoothGatt.discoverServices()).thenReturn(discoverServiceSuccess); connectTestObserver = corePeripheral.connect().test(); ArgumentCaptor<BluetoothGattCallback> gattCaptor = ArgumentCaptor.forClass(BluetoothGattCallback.class); verify(bluetoothDevice).connectGatt(any(), anyBoolean(), gattCaptor.capture()); bluetoothGattCallback = gattCaptor.getValue(); } private void connect() { prepareConnect(true); bluetoothGattCallback.onConnectionStateChange(bluetoothGatt, 0, BluetoothGatt.STATE_CONNECTED); verify(bluetoothGatt).discoverServices(); bluetoothGattCallback.onServicesDiscovered(bluetoothGatt, 0); } private void prepareGatt() { when(bluetoothGattCharacteristic.getUuid()).thenReturn(chrUuid); when(bluetoothGatt.getService(any())).thenReturn(bluetoothGattService); when(bluetoothGattService.getCharacteristic(any())).thenReturn(bluetoothGattCharacteristic); } private void prepareRead(boolean gattReadSuccess) { prepareGatt(); when(bluetoothGatt.readCharacteristic(any())).thenReturn(gattReadSuccess); } private void prepareWrite(boolean setValueSuccess, boolean gattWriteSuccess) { prepareGatt(); when(bluetoothGattCharacteristic.getProperties()).thenReturn(PROPERTY_WRITE_NO_RESPONSE); when(bluetoothGattCharacteristic.setValue(any(byte[].class))).thenReturn(setValueSuccess); when(bluetoothGatt.writeCharacteristic(any())).thenReturn(gattWriteSuccess); } private void prepareNotifications( boolean descriptorNotificationsEnabled, boolean cccdPresent, boolean setValueSuccess, boolean setCharacteristicSuccess, boolean gattWriteDescriptorSuccess) { prepareGatt(); when(bluetoothGattDescriptor.setValue(any())).thenReturn(setValueSuccess); when(bluetoothGattDescriptor.getUuid()).thenReturn(CCCD_UUID); when(bluetoothGattDescriptor.getCharacteristic()).thenReturn(bluetoothGattCharacteristic); if (descriptorNotificationsEnabled) { when(bluetoothGattCharacteristic.getProperties()).thenReturn(PROPERTY_NOTIFY); } else { when(bluetoothGattCharacteristic.getProperties()).thenReturn(0x00); } if (cccdPresent) { when(bluetoothGattCharacteristic.getDescriptor(any())).thenReturn(bluetoothGattDescriptor); } when(bluetoothGatt.setCharacteristicNotification(any(), anyBoolean())) .thenReturn(setCharacteristicSuccess); when(bluetoothGatt.writeDescriptor(any())).thenReturn(gattWriteDescriptorSuccess); } }