From e2634f2285d1c5aeacc6053a4e661f42c93a606d Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Thu, 23 Jul 2020 17:53:38 +0100 Subject: [PATCH 01/21] Added support for custom handling of characteristic+descriptor read/write operations via callbacks --- .../mockrxandroidble/RxBleClientMock.java | 189 +++++++++++++++- .../mockrxandroidble/RxBleConnectionMock.java | 201 +++++++++++++++--- .../mockrxandroidble/RxBleDeviceMock.java | 52 ++++- 3 files changed, 395 insertions(+), 47 deletions(-) diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java index 4a2fb4843..a6176ff78 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java @@ -30,7 +30,10 @@ import java.util.Set; import java.util.UUID; +import io.reactivex.Completable; import io.reactivex.Observable; +import io.reactivex.Single; +import io.reactivex.functions.BiFunction; import io.reactivex.functions.Function; import io.reactivex.functions.Predicate; import io.reactivex.subjects.ReplaySubject; @@ -97,6 +100,11 @@ public static class DeviceBuilder { private RxBleDeviceServices rxBleDeviceServices; private BluetoothDevice bluetoothDevice; private Map> characteristicNotificationSources; + private Map>> characteristicReadCallbacks; + private Map> characteristicWriteCallbacks; + private Map>>> descriptorReadCallbacks; + private Map>> descriptorWriteCallbacks; + RxBleConnectionMock connectionMock; /** * Build a new {@link RxBleDevice}. @@ -108,6 +116,10 @@ public static class DeviceBuilder { public DeviceBuilder() { this.rxBleDeviceServices = new RxBleDeviceServices(new ArrayList()); this.characteristicNotificationSources = new HashMap<>(); + this.characteristicReadCallbacks = new HashMap<>(); + this.characteristicWriteCallbacks = new HashMap<>(); + this.descriptorReadCallbacks = new HashMap<>(); + this.descriptorWriteCallbacks = new HashMap<>(); } /** @@ -143,14 +155,25 @@ public RxBleDevice build() { rxBleDeviceServices, characteristicNotificationSources, bluetoothDevice); - } else { + } else if (connectionMock == null) { rxBleDeviceMock = new RxBleDeviceMock(deviceName, deviceMacAddress, scanRecord, rssi, rxBleDeviceServices, characteristicNotificationSources, + characteristicReadCallbacks, + characteristicWriteCallbacks, + descriptorReadCallbacks, + descriptorWriteCallbacks, bluetoothDevice); + } else { + rxBleDeviceMock = new RxBleDeviceMock(deviceName, + deviceMacAddress, + scanRecord, + rssi, + bluetoothDevice, + connectionMock); } for (BluetoothGattService service : rxBleDeviceServices.getBluetoothGattServices()) { @@ -195,6 +218,75 @@ public DeviceBuilder notificationSource(@NonNull UUID characteristicUUID, @NonNu return this; } + /** + * Set a {@link Function} that will be used to handle characteristic reads for characteristics with a given UUID. The + * function should return a Single which will emit the read data when complete. Calling this method is not required. + * @param characteristicUUID UUID of the characteristic that the callback will handle reads for + * @param readCallback The callback + */ + public DeviceBuilder characteristicReadCallback(@NonNull UUID characteristicUUID, + @NonNull Function> readCallback) { + characteristicReadCallbacks.put(characteristicUUID, readCallback); + return this; + } + + /** + * Set a {@link Function} that will be used to handle characteristic writes for characteristics with a given UUID. The + * function should return a Completable that completes when the write completes. Calling this method is not required. + * @param characteristicUUID UUID of the characteristic that the callback will handle reads for + * @param writeCallback The callback + */ + public DeviceBuilder characteristicWriteCallback(@NonNull UUID characteristicUUID, + @NonNull BiFunction< + BluetoothGattCharacteristic, + byte[], + Completable + > writeCallback) { + characteristicWriteCallbacks.put(characteristicUUID, writeCallback); + return this; + } + + /** + * Set a {@link Function} that will be used to handle descriptor reads for descriptors with a given UUID. The + * function should return a Single which will emit the read data when complete. Calling this method is not required. + * @param characteristicUUID UUID of the characteristic that the descriptor is used in + * @param descriptorUUID UUID of the descriptor that the callback will handle reads for + * @param readCallback The callback + */ + public DeviceBuilder descriptorReadCallback(@NonNull UUID characteristicUUID, + @NonNull UUID descriptorUUID, + @NonNull Function> readCallback) { + Map>> descriptorCallbacks = descriptorReadCallbacks + .get(characteristicUUID); + if (descriptorCallbacks == null) { + descriptorCallbacks = new HashMap<>(); + descriptorReadCallbacks.put(characteristicUUID, descriptorCallbacks); + } + descriptorCallbacks.put(descriptorUUID, readCallback); + return this; + } + + /** + * Set a {@link Function} that will be used to handle descriptor writes for descriptors with a given UUID. The + * function should return a Completable that completes when the write completes. Calling this method is not required. + * @param characteristicUUID UUID of the characteristic that the descriptor is used in + * @param descriptorUUID UUID of the descriptor that the callback will handle reads for + * @param writeCallback The callback + */ + public DeviceBuilder descriptorWriteCallback(@NonNull UUID characteristicUUID, + @NonNull UUID descriptorUUID, + @NonNull BiFunction writeCallback) { + Map> descriptorCallbacks = descriptorWriteCallbacks + .get(characteristicUUID); + if (descriptorCallbacks == null) { + descriptorCallbacks = new HashMap<>(); + descriptorWriteCallbacks.put(characteristicUUID, descriptorCallbacks); + } + descriptorCallbacks.put(descriptorUUID, writeCallback); + return this; + } + /** * Set a rssi that will be reported. Calling this method is required. */ @@ -218,6 +310,11 @@ public DeviceBuilder scanRecord(@NonNull ScanRecord scanRecord) { this.scanRecord = scanRecord; return this; } + + public DeviceBuilder connection(@NonNull RxBleConnectionMock connectionMock) { + this.connectionMock = connectionMock; + return this; + } } public static class CharacteristicsBuilder { @@ -232,6 +329,49 @@ public CharacteristicsBuilder() { this.bluetoothGattCharacteristics = new ArrayList<>(); } + /** + * Adds a {@link BluetoothGattCharacteristic} + * + * @param characteristic The characteristic to add + */ + public CharacteristicsBuilder addCharacteristic(BluetoothGattCharacteristic characteristic) { + this.bluetoothGattCharacteristics.add(characteristic); + return this; + } + + /** + * Adds a {@link BluetoothGattCharacteristic} with specified parameters. + * + * @param uuid characteristic UUID + * @param data locally stored value of the characteristic + * @param properties OR-ed {@link BluetoothGattCharacteristic} property constants + * @param descriptors list of characteristic descriptors. Use {@link DescriptorsBuilder} to create them. + */ + public CharacteristicsBuilder addCharacteristic(@NonNull UUID uuid, + @NonNull byte[] data, + int properties, + List descriptors) { + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(uuid, properties, 0); + for (BluetoothGattDescriptor descriptor : descriptors) { + characteristic.addDescriptor(descriptor); + } + characteristic.setValue(data); + return addCharacteristic(characteristic); + } + + /** + * Adds a {@link BluetoothGattCharacteristic} with specified parameters. + * + * @param uuid characteristic UUID + * @param data locally stored value of the characteristic + * @param properties OR-ed {@link BluetoothGattCharacteristic} property constants + */ + public CharacteristicsBuilder addCharacteristic(@NonNull UUID uuid, + @NonNull byte[] data, + int properties) { + return addCharacteristic(uuid, data, properties, new ArrayList()); + } + /** * Adds a {@link BluetoothGattCharacteristic} with specified parameters. * @@ -245,6 +385,19 @@ public CharacteristicsBuilder addCharacteristic(@NonNull UUID uuid, return addCharacteristic(uuid, data, 0, descriptors); } + /** + * Adds a {@link BluetoothGattCharacteristic} with specified parameters. + * + * @param uuid characteristic UUID + * @param data locally stored value of the characteristic + * @param descriptors list of characteristic descriptors. Use {@link DescriptorsBuilder} to create them. + */ + public CharacteristicsBuilder addCharacteristic(@NonNull UUID uuid, + @NonNull byte[] data, + BluetoothGattDescriptor... descriptors) { + return addCharacteristic(uuid, data, 0, descriptors); + } + /** * Adds a {@link BluetoothGattCharacteristic} with specified parameters. * @@ -256,14 +409,19 @@ public CharacteristicsBuilder addCharacteristic(@NonNull UUID uuid, public CharacteristicsBuilder addCharacteristic(@NonNull UUID uuid, @NonNull byte[] data, int properties, - List descriptors) { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(uuid, properties, 0); - for (BluetoothGattDescriptor descriptor : descriptors) { - characteristic.addDescriptor(descriptor); - } - characteristic.setValue(data); - this.bluetoothGattCharacteristics.add(characteristic); - return this; + BluetoothGattDescriptor... descriptors) { + return addCharacteristic(uuid, data, properties, Arrays.asList(descriptors)); + } + + /** + * Adds a {@link BluetoothGattCharacteristic} with specified parameters. + * + * @param uuid characteristic UUID + * @param data locally stored value of the characteristic + */ + public CharacteristicsBuilder addCharacteristic(@NonNull UUID uuid, + @NonNull byte[] data) { + return addCharacteristic(uuid, data, 0); } /** @@ -286,6 +444,16 @@ public DescriptorsBuilder() { this.bluetoothGattDescriptors = new ArrayList<>(); } + /** + * Adds a {@link BluetoothGattDescriptor}. + * + * @param descriptor the descriptor + */ + public DescriptorsBuilder addDescriptor(@NonNull BluetoothGattDescriptor descriptor) { + bluetoothGattDescriptors.add(descriptor); + return this; + } + /** * Adds a {@link BluetoothGattDescriptor} with specified parameters. * @@ -295,8 +463,7 @@ public DescriptorsBuilder() { public DescriptorsBuilder addDescriptor(@NonNull UUID uuid, @NonNull byte[] data) { BluetoothGattDescriptor bluetoothGattDescriptor = new BluetoothGattDescriptor(uuid, 0); bluetoothGattDescriptor.setValue(data); - bluetoothGattDescriptors.add(bluetoothGattDescriptor); - return this; + return addDescriptor(bluetoothGattDescriptor); } /** diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java index 35c626b25..8bad55456 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java @@ -28,6 +28,7 @@ import io.reactivex.Single; import io.reactivex.SingleSource; import io.reactivex.functions.Action; +import io.reactivex.functions.BiFunction; import io.reactivex.functions.Consumer; import io.reactivex.functions.Function; import io.reactivex.functions.Predicate; @@ -58,14 +59,25 @@ public class RxBleConnectionMock implements RxBleConnection { private int rssi; private int currentMtu = 23; private Map> characteristicNotificationSources; - + private Map>> characteristicReadCallbacks; + private Map> characteristicWriteCallbacks; + private Map>>> descriptorReadCallbacks; + private Map>> descriptorWriteCallbacks; public RxBleConnectionMock(RxBleDeviceServices rxBleDeviceServices, int rssi, - Map> characteristicNotificationSources) { + Map> characteristicNotificationSources, + Map>> characteristicReadCallbacks, + Map> characteristicWriteCallbacks, + Map>>> descriptorReadCallbacks, + Map>> descriptorWriteCallbacks) { this.rxBleDeviceServices = rxBleDeviceServices; this.rssi = rssi; this.characteristicNotificationSources = characteristicNotificationSources; + this.characteristicReadCallbacks = characteristicReadCallbacks; + this.characteristicWriteCallbacks = characteristicWriteCallbacks; + this.descriptorReadCallbacks = descriptorReadCallbacks; + this.descriptorWriteCallbacks = descriptorWriteCallbacks; } @Override @@ -115,29 +127,71 @@ public SingleSource apply(RxBleDeviceServ @Override public Single readCharacteristic(@NonNull UUID characteristicUuid) { - return getCharacteristic(characteristicUuid).map(new Function() { + final Function> readCallback = characteristicReadCallbacks.get(characteristicUuid); + Single characteristic = getCharacteristic(characteristicUuid); + if (readCallback == null) { + return characteristic.map(new Function() { + @Override + public byte[] apply(BluetoothGattCharacteristic bluetoothGattCharacteristic) throws Exception { + return bluetoothGattCharacteristic.getValue(); + } + }); + } + return characteristic.flatMap(new Function>() { @Override - public byte[] apply(BluetoothGattCharacteristic bluetoothGattCharacteristic) throws Exception { - return bluetoothGattCharacteristic.getValue(); + public SingleSource apply(final BluetoothGattCharacteristic characteristic) throws Exception { + return Single.just(characteristic).flatMap(readCallback).doOnSuccess(new Consumer() { + @Override + public void accept(byte[] bytes) throws Exception { + characteristic.setValue(bytes); + } + }); } }); } @Override - public Single readCharacteristic(@NonNull BluetoothGattCharacteristic characteristic) { - return Single.just(characteristic.getValue()); + public Single readCharacteristic(@NonNull final BluetoothGattCharacteristic characteristic) { + Function> readCallback = characteristicReadCallbacks.get(characteristic.getUuid()); + if (readCallback == null) { + return Single.just(characteristic.getValue()); + } + return Single.just(characteristic).flatMap(readCallback).doOnSuccess(new Consumer() { + @Override + public void accept(byte[] bytes) throws Exception { + characteristic.setValue(bytes); + } + }); } @Override public Single readDescriptor(@NonNull final UUID serviceUuid, @NonNull final UUID characteristicUuid, @NonNull final UUID descriptorUuid) { - return discoverServices() + final Single descriptor = discoverServices() .flatMap(new Function>() { @Override public SingleSource apply(RxBleDeviceServices rxBleDeviceServices) { return rxBleDeviceServices.getDescriptor(serviceUuid, characteristicUuid, descriptorUuid); } - }) + }); + Map>> descriptorCallbacks = descriptorReadCallbacks.get(characteristicUuid); + if (descriptorCallbacks != null) { + final Function> readCallback = descriptorCallbacks.get(descriptorUuid); + if (readCallback != null) { + return descriptor.flatMap(new Function>() { + @Override + public SingleSource apply(final BluetoothGattDescriptor descriptor) throws Exception { + return Single.just(descriptor).flatMap(readCallback).doOnSuccess(new Consumer() { + @Override + public void accept(byte[] bytes) throws Exception { + descriptor.setValue(bytes); + } + }); + } + }); + } + } + return descriptor .map(new Function() { @Override public byte[] apply(BluetoothGattDescriptor bluetoothGattDescriptor) { @@ -147,7 +201,20 @@ public byte[] apply(BluetoothGattDescriptor bluetoothGattDescriptor) { } @Override - public Single readDescriptor(@NonNull BluetoothGattDescriptor descriptor) { + public Single readDescriptor(@NonNull final BluetoothGattDescriptor descriptor) { + Map>> descriptorCallbacks = descriptorReadCallbacks + .get(descriptor.getCharacteristic().getUuid()); + if (descriptorCallbacks != null) { + Function> readCallback = descriptorCallbacks.get(descriptor.getUuid()); + if (readCallback != null) { + return Single.just(descriptor).flatMap(readCallback).doOnSuccess(new Consumer() { + @Override + public void accept(byte[] bytes) throws Exception { + descriptor.setValue(bytes); + } + }); + } + } return Single.just(descriptor.getValue()); } @@ -255,9 +322,60 @@ public Observable> setupIndication(@NonNull BluetoothGattChar } @Override - public Single writeCharacteristic(@NonNull BluetoothGattCharacteristic bluetoothGattCharacteristic, @NonNull byte[] data) { - bluetoothGattCharacteristic.setValue(data); - return Single.just(data); + public Single writeCharacteristic(@NonNull BluetoothGattCharacteristic bluetoothGattCharacteristic, + @NonNull final byte[] data) { + final BiFunction writeCallback = characteristicWriteCallbacks + .get(bluetoothGattCharacteristic.getUuid()); + if (writeCallback == null) { + bluetoothGattCharacteristic.setValue(data); + return Single.just(data); + } + // wrap characteristic in single so exceptions from writeCallback are handled for us + return Single.just(bluetoothGattCharacteristic) + .flatMap(new Function>() { + @Override + public SingleSource apply(final BluetoothGattCharacteristic characteristic) throws Exception { + return writeCallback + .apply(characteristic, data) + .toSingleDefault(data) + .doOnSuccess(new Consumer() { + @Override + public void accept(byte[] bytes) throws Exception { + characteristic.setValue(bytes); + } + }); + } + }); + } + + @Override + public Single writeCharacteristic(@NonNull UUID characteristicUuid, @NonNull final byte[] data) { + final BiFunction writeCallback = characteristicWriteCallbacks + .get(characteristicUuid); + Single characteristic = getCharacteristic(characteristicUuid); + if (writeCallback == null) { + return characteristic.map(new Function() { + @Override + public byte[] apply(BluetoothGattCharacteristic characteristic) { + characteristic.setValue(data); + return data; + } + }); + } + return characteristic.flatMap(new Function>() { + @Override + public SingleSource apply(final BluetoothGattCharacteristic characteristic) throws Exception { + return writeCallback + .apply(characteristic, data) + .toSingleDefault(data) + .doOnSuccess(new Consumer() { + @Override + public void accept(byte[] bytes) throws Exception { + characteristic.setValue(bytes); + } + }); + } + }); } @Override @@ -354,39 +472,64 @@ public boolean test(Object object) { }; } - @Override - public Single writeCharacteristic(@NonNull UUID characteristicUuid, @NonNull final byte[] data) { - return getCharacteristic(characteristicUuid) - .map(new Function() { - @Override - public byte[] apply(BluetoothGattCharacteristic characteristic) { - characteristic.setValue(data); - return data; - } - }); - } - @Override public Completable writeDescriptor(@NonNull final UUID serviceUuid, @NonNull final UUID characteristicUuid, @NonNull final UUID descriptorUuid, @NonNull final byte[] data) { - return discoverServices() + Single descriptor = discoverServices() .flatMap(new Function>() { @Override public SingleSource apply(RxBleDeviceServices rxBleDeviceServices) { return rxBleDeviceServices.getDescriptor(serviceUuid, characteristicUuid, descriptorUuid); } - }) - .doOnSuccess(new Consumer() { + }); + Map> descriptorCallbacks = descriptorWriteCallbacks + .get(characteristicUuid); + if (descriptorCallbacks != null) { + final BiFunction writeCallback = descriptorCallbacks.get(descriptorUuid); + if (writeCallback != null) { + return descriptor.flatMapCompletable(new Function() { + @Override + public Completable apply(final BluetoothGattDescriptor descriptor) throws Exception { + return writeCallback.apply(descriptor, data).doOnComplete(new Action() { + @Override + public void run() throws Exception { + descriptor.setValue(data); + } + }); + } + }); + } + } + return descriptor.doOnSuccess(new Consumer() { @Override public void accept(BluetoothGattDescriptor bluetoothGattDescriptor) throws Exception { bluetoothGattDescriptor.setValue(data); } }) - .toCompletable(); + .ignoreElement(); } @Override public Completable writeDescriptor(@NonNull final BluetoothGattDescriptor descriptor, @NonNull final byte[] data) { + Map> descriptorCallbacks = descriptorWriteCallbacks + .get(descriptor.getCharacteristic().getUuid()); + if (descriptorCallbacks != null) { + final BiFunction writeCallback = descriptorCallbacks.get(descriptor.getUuid()); + if (writeCallback != null) { + // wrap descriptor in single so exceptions from writeCallback are handled for us + return Single.just(descriptor).flatMapCompletable(new Function() { + @Override + public Completable apply(final BluetoothGattDescriptor descriptor) throws Exception { + return writeCallback.apply(descriptor, data).doOnComplete(new Action() { + @Override + public void run() throws Exception { + descriptor.setValue(data); + } + }); + } + }); + } + } return Completable.fromAction(new Action() { @Override public void run() throws Exception { diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java index e23ea9e0e..c0a0fd5d8 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java @@ -1,6 +1,8 @@ package com.polidea.rxandroidble2.mockrxandroidble; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; import androidx.annotation.Nullable; @@ -12,16 +14,21 @@ import com.polidea.rxandroidble2.scan.ScanRecord; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; +import io.reactivex.Completable; import io.reactivex.Observable; +import io.reactivex.Single; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Action; +import io.reactivex.functions.BiFunction; import io.reactivex.functions.Consumer; +import io.reactivex.functions.Function; import io.reactivex.subjects.BehaviorSubject; import static com.polidea.rxandroidble2.RxBleConnection.RxBleConnectionState.CONNECTED; @@ -54,7 +61,11 @@ public RxBleDeviceMock(String name, this.macAddress = macAddress; this.rxBleConnection = new RxBleConnectionMock(rxBleDeviceServices, rssi, - characteristicNotificationSources); + characteristicNotificationSources, + new HashMap>>(), + new HashMap>(), + new HashMap>>>(), + new HashMap>>()); this.rssi = rssi; this.legacyScanRecord = scanRecord; this.advertisedUUIDs = new ArrayList<>(); @@ -65,20 +76,47 @@ public RxBleDeviceMock(String name, String macAddress, ScanRecord scanRecord, Integer rssi, - RxBleDeviceServices rxBleDeviceServices, - Map> characteristicNotificationSources, - @Nullable BluetoothDevice bluetoothDevice) { + @Nullable BluetoothDevice bluetoothDevice, + RxBleConnectionMock connectionMock + ) { this.name = name; this.macAddress = macAddress; - this.rxBleConnection = new RxBleConnectionMock(rxBleDeviceServices, - rssi, - characteristicNotificationSources); + this.rxBleConnection = connectionMock; this.rssi = rssi; this.scanRecord = scanRecord; this.advertisedUUIDs = new ArrayList<>(); this.bluetoothDevice = bluetoothDevice; } + public RxBleDeviceMock(String name, + String macAddress, + ScanRecord scanRecord, + Integer rssi, + RxBleDeviceServices rxBleDeviceServices, + Map> characteristicNotificationSources, + Map>> characteristicReadCallbacks, + Map> characteristicWriteCallbacks, + Map>>> descriptorReadCallbacks, + Map>> descriptorWriteCallbacks, + @Nullable BluetoothDevice bluetoothDevice) { + this( + name, + macAddress, + scanRecord, + rssi, + bluetoothDevice, + new RxBleConnectionMock( + rxBleDeviceServices, + rssi, + characteristicNotificationSources, + characteristicReadCallbacks, + characteristicWriteCallbacks, + descriptorReadCallbacks, + descriptorWriteCallbacks + ) + ); + } + public void addAdvertisedUUID(UUID advertisedUUID) { advertisedUUIDs.add(advertisedUUID); } From 05db374e89a1909d424f12ccc10e9f951347f966 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Fri, 24 Jul 2020 11:35:31 +0100 Subject: [PATCH 02/21] Moved DeviceBuilder to RxBleDeviceMock.Builder --- .../mockrxandroidble/RxBleClientMock.java | 146 ++---------------- .../mockrxandroidble/RxBleDeviceMock.java | 137 ++++++++++++++++ 2 files changed, 148 insertions(+), 135 deletions(-) diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java index ec7158929..21db25515 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java @@ -61,7 +61,7 @@ public Builder setDeviceDiscoveryObservable(@NonNull Observable /** * Add a {@link RxBleDevice} to the mock client. * - * @param rxBleDevice device that the mocked client should contain. Use {@link DeviceBuilder} to create them. + * @param rxBleDevice device that the mocked client should contain. Use {@link RxBleDeviceMock.Builder} to create them. */ public Builder addDevice(@NonNull RxBleDevice rxBleDevice) { this.discoverableDevicesSubject.onNext((RxBleDeviceMock) rxBleDevice); @@ -71,7 +71,7 @@ public Builder addDevice(@NonNull RxBleDevice rxBleDevice) { /** * Add a {@link RxBleDevice} to the list of bonded devices. * - * @param rxBleDevice device that the mocked client should contain. Use {@link DeviceBuilder} to create them. + * @param rxBleDevice device that the mocked client should contain. Use {@link RxBleDeviceMock.Builder} to create them. */ public Builder addBondedDevice(@NonNull RxBleDevice rxBleDevice) { bondedDevices.add(rxBleDevice); @@ -86,138 +86,14 @@ public RxBleClientMock build() { } } - public static class DeviceBuilder { - - private int rssi = -1; - private String deviceName; - private String deviceMacAddress; - private byte[] legacyScanRecord; - private ScanRecord scanRecord; - private RxBleDeviceServices rxBleDeviceServices; - private BluetoothDevice bluetoothDevice; - private Map> characteristicNotificationSources; - - /** - * Build a new {@link RxBleDevice}. - *

- * Calling {@link #scanRecord}, {@link #rssi} and {@link #deviceMacAddress} - * is required before calling {@link #build()}. All other methods - * are optional. - */ - public DeviceBuilder() { - this.rxBleDeviceServices = new RxBleDeviceServices(new ArrayList()); - this.characteristicNotificationSources = new HashMap<>(); - } - - /** - * Add a {@link BluetoothGattService} to the device. Calling this method is not required. - * - * @param uuid service UUID - * @param characteristics characteristics that the service should report. Use {@link CharacteristicsBuilder} to create them. - */ - public DeviceBuilder addService(@NonNull UUID uuid, @NonNull List characteristics) { - BluetoothGattService bluetoothGattService = new BluetoothGattService(uuid, 0); - for (BluetoothGattCharacteristic characteristic : characteristics) { - bluetoothGattService.addCharacteristic(characteristic); - } - rxBleDeviceServices.getBluetoothGattServices().add(bluetoothGattService); - return this; - } - - /** - * Create the {@link RxBleDeviceMock} instance using the configured values. - */ - public RxBleDevice build() { - if (this.rssi == -1) throw new IllegalStateException("Rssi is required. DeviceBuilder#rssi should be called."); - if (this.deviceMacAddress == null) throw new IllegalStateException("DeviceMacAddress required." - + " DeviceBuilder#deviceMacAddress should be called."); - if (this.scanRecord == null && this.legacyScanRecord == null) - throw new IllegalStateException("ScanRecord required. DeviceBuilder#scanRecord should be called."); - RxBleDeviceMock rxBleDeviceMock; - if (scanRecord == null) { - rxBleDeviceMock = new RxBleDeviceMock(deviceName, - deviceMacAddress, - legacyScanRecord, - rssi, - rxBleDeviceServices, - characteristicNotificationSources, - bluetoothDevice); - } else { - rxBleDeviceMock = new RxBleDeviceMock(deviceName, - deviceMacAddress, - scanRecord, - rssi, - rxBleDeviceServices, - characteristicNotificationSources, - bluetoothDevice); - } - - for (BluetoothGattService service : rxBleDeviceServices.getBluetoothGattServices()) { - rxBleDeviceMock.addAdvertisedUUID(service.getUuid()); - } - return rxBleDeviceMock; - } - - /** - * Set a device mac address. Calling this method is required. - */ - public DeviceBuilder deviceMacAddress(@NonNull String deviceMacAddress) { - this.deviceMacAddress = deviceMacAddress; - return this; - } - - /** - * Set a device name. Calling this method is not required. - */ - public DeviceBuilder deviceName(@NonNull String deviceName) { - this.deviceName = deviceName; - return this; - } - - /** - * Sets a bluetooth device. Calling this method is not required. - */ - public DeviceBuilder bluetoothDevice(@NonNull BluetoothDevice bluetoothDevice) { - this.bluetoothDevice = bluetoothDevice; - return this; - } - - /** - * Set an {@link Observable} that will be used to fire characteristic change notifications. It will be subscribed to after - * a call to {@link com.polidea.rxandroidble2.RxBleConnection#setupNotification(UUID)}. Calling this method is not required. - * - * @param characteristicUUID UUID of the characteristic that will be observed for notifications - * @param sourceObservable Observable that will be subscribed to in order to receive characteristic change notifications - */ - public DeviceBuilder notificationSource(@NonNull UUID characteristicUUID, @NonNull Observable sourceObservable) { - characteristicNotificationSources.put(characteristicUUID, sourceObservable); - return this; - } - - /** - * Set a rssi that will be reported. Calling this method is required. - */ - public DeviceBuilder rssi(int rssi) { - this.rssi = rssi; - return this; - } - - /** - * Set a BLE scan record. Calling either this method or the other {@link #scanRecord(ScanRecord) scanRecord} method is required. - */ - public DeviceBuilder scanRecord(@NonNull byte[] scanRecord) { - this.legacyScanRecord = scanRecord; - return this; - } - - /** - * Set a BLE scan record. Calling this method is required. - */ - public DeviceBuilder scanRecord(@NonNull ScanRecord scanRecord) { - this.scanRecord = scanRecord; - return this; - } - } + /** + * Device builder class. + * @deprecated Use {@link RxBleDeviceMock.Builder} + */ +// @Deprecated +// public static class DeviceBuilder extends RxBleDeviceMock.Builder { +// +// } public static class CharacteristicsBuilder { @@ -225,7 +101,7 @@ public static class CharacteristicsBuilder { /** * Build a new {@link BluetoothGattCharacteristic} list. - * Should be used in pair with {@link DeviceBuilder#addService} + * Should be used in pair with {@link RxBleDeviceMock.Builder#addService} */ public CharacteristicsBuilder() { this.bluetoothGattCharacteristics = new ArrayList<>(); diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java index e23ea9e0e..a769b8d4f 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java @@ -1,7 +1,10 @@ package com.polidea.rxandroidble2.mockrxandroidble; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.polidea.rxandroidble2.RxBleConnection; @@ -12,6 +15,7 @@ import com.polidea.rxandroidble2.scan.ScanRecord; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -79,6 +83,139 @@ public RxBleDeviceMock(String name, this.bluetoothDevice = bluetoothDevice; } + public static class Builder { + + private int rssi = -1; + private String deviceName; + private String deviceMacAddress; + private byte[] legacyScanRecord; + private ScanRecord scanRecord; + private RxBleDeviceServices rxBleDeviceServices; + private BluetoothDevice bluetoothDevice; + private Map> characteristicNotificationSources; + + /** + * Build a new {@link RxBleDevice}. + *

+ * Calling {@link #scanRecord}, {@link #rssi} and {@link #deviceMacAddress} + * is required before calling {@link #build()}. All other methods + * are optional. + */ + public Builder() { + this.rxBleDeviceServices = new RxBleDeviceServices(new ArrayList()); + this.characteristicNotificationSources = new HashMap<>(); + } + + /** + * Add a {@link BluetoothGattService} to the device. Calling this method is not required. + * + * @param uuid service UUID + * @param characteristics characteristics that the service should report. Use {@link RxBleClientMock.CharacteristicsBuilder} to create them. + */ + public Builder addService(@NonNull UUID uuid, @NonNull List characteristics) { + BluetoothGattService bluetoothGattService = new BluetoothGattService(uuid, 0); + for (BluetoothGattCharacteristic characteristic : characteristics) { + bluetoothGattService.addCharacteristic(characteristic); + } + rxBleDeviceServices.getBluetoothGattServices().add(bluetoothGattService); + return this; + } + + /** + * Create the {@link RxBleDeviceMock} instance using the configured values. + */ + public RxBleDevice build() { + if (this.rssi == -1) throw new IllegalStateException("Rssi is required. Builder#rssi should be called."); + if (this.deviceMacAddress == null) throw new IllegalStateException("DeviceMacAddress required." + + " Builder#deviceMacAddress should be called."); + if (this.scanRecord == null && this.legacyScanRecord == null) + throw new IllegalStateException("ScanRecord required. Builder#scanRecord should be called."); + RxBleDeviceMock rxBleDeviceMock; + if (scanRecord == null) { + rxBleDeviceMock = new RxBleDeviceMock(deviceName, + deviceMacAddress, + legacyScanRecord, + rssi, + rxBleDeviceServices, + characteristicNotificationSources, + bluetoothDevice); + } else { + rxBleDeviceMock = new RxBleDeviceMock(deviceName, + deviceMacAddress, + scanRecord, + rssi, + rxBleDeviceServices, + characteristicNotificationSources, + bluetoothDevice); + } + + for (BluetoothGattService service : rxBleDeviceServices.getBluetoothGattServices()) { + rxBleDeviceMock.addAdvertisedUUID(service.getUuid()); + } + return rxBleDeviceMock; + } + + /** + * Set a device mac address. Calling this method is required. + */ + public Builder deviceMacAddress(@NonNull String deviceMacAddress) { + this.deviceMacAddress = deviceMacAddress; + return this; + } + + /** + * Set a device name. Calling this method is not required. + */ + public Builder deviceName(@NonNull String deviceName) { + this.deviceName = deviceName; + return this; + } + + /** + * Sets a bluetooth device. Calling this method is not required. + */ + public Builder bluetoothDevice(@NonNull BluetoothDevice bluetoothDevice) { + this.bluetoothDevice = bluetoothDevice; + return this; + } + + /** + * Set an {@link Observable} that will be used to fire characteristic change notifications. It will be subscribed to after + * a call to {@link com.polidea.rxandroidble2.RxBleConnection#setupNotification(UUID)}. Calling this method is not required. + * + * @param characteristicUUID UUID of the characteristic that will be observed for notifications + * @param sourceObservable Observable that will be subscribed to in order to receive characteristic change notifications + */ + public Builder notificationSource(@NonNull UUID characteristicUUID, @NonNull Observable sourceObservable) { + characteristicNotificationSources.put(characteristicUUID, sourceObservable); + return this; + } + + /** + * Set a rssi that will be reported. Calling this method is required. + */ + public Builder rssi(int rssi) { + this.rssi = rssi; + return this; + } + + /** + * Set a BLE scan record. Calling either this method or the other {@link #scanRecord(ScanRecord) scanRecord} method is required. + */ + public Builder scanRecord(@NonNull byte[] scanRecord) { + this.legacyScanRecord = scanRecord; + return this; + } + + /** + * Set a BLE scan record. Calling this method is required. + */ + public Builder scanRecord(@NonNull ScanRecord scanRecord) { + this.scanRecord = scanRecord; + return this; + } + } + public void addAdvertisedUUID(UUID advertisedUUID) { advertisedUUIDs.add(advertisedUUID); } From f019a1e27b247d9f9761c1db7d1301bc022a585e Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Fri, 24 Jul 2020 11:45:33 +0100 Subject: [PATCH 03/21] Structural changes to creating devices --- .../mockrxandroidble/RxBleClientMock.java | 13 +- .../mockrxandroidble/RxBleConnectionMock.java | 89 +++- .../mockrxandroidble/RxBleDeviceMock.java | 105 ++-- .../RxBleClientMockLegacyTest.groovy | 464 ++++++++++++++++++ .../RxBleClientMockTest.groovy | 62 +-- 5 files changed, 623 insertions(+), 110 deletions(-) create mode 100644 mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockLegacyTest.groovy diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java index 21db25515..1cf3855b0 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java @@ -1,16 +1,13 @@ package com.polidea.rxandroidble2.mockrxandroidble; -import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; -import android.bluetooth.BluetoothGattService; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.polidea.rxandroidble2.RxBleClient; import com.polidea.rxandroidble2.RxBleDevice; -import com.polidea.rxandroidble2.RxBleDeviceServices; import com.polidea.rxandroidble2.RxBleScanResult; import com.polidea.rxandroidble2.scan.BackgroundScanner; import com.polidea.rxandroidble2.scan.ScanCallbackType; @@ -22,10 +19,8 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.UUID; @@ -90,10 +85,10 @@ public RxBleClientMock build() { * Device builder class. * @deprecated Use {@link RxBleDeviceMock.Builder} */ -// @Deprecated -// public static class DeviceBuilder extends RxBleDeviceMock.Builder { -// -// } + @Deprecated + public static class DeviceBuilder extends RxBleDeviceMock.Builder { + + } public static class CharacteristicsBuilder { diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java index 35c626b25..3d42ef15e 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java @@ -3,6 +3,8 @@ import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import androidx.annotation.NonNull; + +import android.bluetooth.BluetoothGattService; import android.util.Log; import com.polidea.rxandroidble2.ConnectionParameters; @@ -15,7 +17,9 @@ import com.polidea.rxandroidble2.internal.connection.ImmediateSerializedBatchAckStrategy; import com.polidea.rxandroidble2.internal.util.ObservableUtil; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.Callable; @@ -27,7 +31,9 @@ import io.reactivex.ObservableSource; import io.reactivex.Single; import io.reactivex.SingleSource; +import io.reactivex.disposables.Disposable; import io.reactivex.functions.Action; +import io.reactivex.functions.BiFunction; import io.reactivex.functions.Consumer; import io.reactivex.functions.Function; import io.reactivex.functions.Predicate; @@ -91,6 +97,30 @@ public int getMtu() { return currentMtu; } + public int getRssi() { + return rssi; + } + + RxBleDeviceServices getRxBleDeviceServices() { + return rxBleDeviceServices; + } + + List getBluetoothGattServices() { + return rxBleDeviceServices.getBluetoothGattServices(); + } + + List getServiceUuids() { + List uuids = new ArrayList<>(); + for (BluetoothGattService service : getBluetoothGattServices()) { + uuids.add(service.getUuid()); + } + return uuids; + } + + Map> getCharacteristicNotificationSources() { + return characteristicNotificationSources; + } + @Override public Single discoverServices() { return Single.just(rxBleDeviceServices); @@ -418,7 +448,7 @@ public Observable observeConnectionParametersUpdates() { private void dismissCharacteristicNotification(UUID characteristicUuid, NotificationSetupMode setupMode, boolean isIndication) { notificationObservableMap.remove(characteristicUuid); - setupCharacteristicNotification(characteristicUuid, setupMode, false, isIndication) + final Disposable subscribe = setupCharacteristicNotification(characteristicUuid, setupMode, false, isIndication) .subscribe( Functions.EMPTY_ACTION, Functions.emptyConsumer() @@ -444,7 +474,6 @@ public BluetoothGattDescriptor apply(BluetoothGattCharacteristic bluetoothGattCh }); } - @NonNull private Observable observeOnCharacteristicChangeCallbacks(UUID characteristicUuid) { return characteristicNotificationSources.get(characteristicUuid); } @@ -480,4 +509,60 @@ public Observable queue(@NonNull RxBleCustomOperation operation) { public Observable queue(@NonNull RxBleCustomOperation operation, Priority priority) { throw new UnsupportedOperationException("Mock does not support queuing custom operation."); } + + public static class Builder { + private RxBleDeviceServices rxBleDeviceServices; + private int rssi; + private Map> characteristicNotificationSources = new HashMap<>(); + + public Builder() { + + } + + public RxBleConnectionMock build() { + if (this.rssi == -1) throw new IllegalStateException("Rssi is required. DeviceBuilder#rssi should be called."); + return new RxBleConnectionMock( + rxBleDeviceServices, + rssi, + characteristicNotificationSources + ); + } + + /** + * Set a rssi that will be reported. Calling this method is required. + */ + public Builder rssi(int rssi) { + this.rssi = rssi; + this.rxBleDeviceServices = new RxBleDeviceServices(new ArrayList()); + return this; + } + + /** + * Add a {@link BluetoothGattService} to the device. Calling this method is not required. + * + * @param uuid service UUID + * @param characteristics characteristics that the service should report. Use {@link RxBleClientMock.CharacteristicsBuilder} to + * create them. + */ + public Builder addService(@NonNull UUID uuid, @NonNull List characteristics) { + BluetoothGattService bluetoothGattService = new BluetoothGattService(uuid, 0); + for (BluetoothGattCharacteristic characteristic : characteristics) { + bluetoothGattService.addCharacteristic(characteristic); + } + rxBleDeviceServices.getBluetoothGattServices().add(bluetoothGattService); + return this; + } + + /** + * Set an {@link Observable} that will be used to fire characteristic change notifications. It will be subscribed to after + * a call to {@link com.polidea.rxandroidble2.RxBleConnection#setupNotification(UUID)}. Calling this method is not required. + * + * @param characteristicUUID UUID of the characteristic that will be observed for notifications + * @param sourceObservable Observable that will be subscribed to in order to receive characteristic change notifications + */ + public Builder notificationSource(@NonNull UUID characteristicUUID, @NonNull Observable sourceObservable) { + characteristicNotificationSources.put(characteristicUUID, sourceObservable); + return this; + } + } } diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java index a769b8d4f..967a44e10 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java @@ -68,31 +68,27 @@ public RxBleDeviceMock(String name, public RxBleDeviceMock(String name, String macAddress, ScanRecord scanRecord, - Integer rssi, - RxBleDeviceServices rxBleDeviceServices, - Map> characteristicNotificationSources, - @Nullable BluetoothDevice bluetoothDevice) { + @Nullable BluetoothDevice bluetoothDevice, + RxBleConnectionMock connectionMock + ) { this.name = name; this.macAddress = macAddress; - this.rxBleConnection = new RxBleConnectionMock(rxBleDeviceServices, - rssi, - characteristicNotificationSources); - this.rssi = rssi; + this.rxBleConnection = connectionMock; + this.rssi = connectionMock.getRssi(); this.scanRecord = scanRecord; - this.advertisedUUIDs = new ArrayList<>(); + this.advertisedUUIDs = connectionMock.getServiceUuids(); this.bluetoothDevice = bluetoothDevice; } public static class Builder { - private int rssi = -1; private String deviceName; private String deviceMacAddress; private byte[] legacyScanRecord; private ScanRecord scanRecord; - private RxBleDeviceServices rxBleDeviceServices; private BluetoothDevice bluetoothDevice; - private Map> characteristicNotificationSources; + RxBleConnectionMock connectionMock; + RxBleConnectionMock.Builder connectionMockBuilder; /** * Build a new {@link RxBleDevice}. @@ -102,57 +98,54 @@ public static class Builder { * are optional. */ public Builder() { - this.rxBleDeviceServices = new RxBleDeviceServices(new ArrayList()); - this.characteristicNotificationSources = new HashMap<>(); - } - - /** - * Add a {@link BluetoothGattService} to the device. Calling this method is not required. - * - * @param uuid service UUID - * @param characteristics characteristics that the service should report. Use {@link RxBleClientMock.CharacteristicsBuilder} to create them. - */ - public Builder addService(@NonNull UUID uuid, @NonNull List characteristics) { - BluetoothGattService bluetoothGattService = new BluetoothGattService(uuid, 0); - for (BluetoothGattCharacteristic characteristic : characteristics) { - bluetoothGattService.addCharacteristic(characteristic); - } - rxBleDeviceServices.getBluetoothGattServices().add(bluetoothGattService); - return this; + this.connectionMockBuilder = new RxBleConnectionMock.Builder(); } /** * Create the {@link RxBleDeviceMock} instance using the configured values. */ public RxBleDevice build() { - if (this.rssi == -1) throw new IllegalStateException("Rssi is required. Builder#rssi should be called."); if (this.deviceMacAddress == null) throw new IllegalStateException("DeviceMacAddress required." - + " Builder#deviceMacAddress should be called."); + + " DeviceBuilder#deviceMacAddress should be called."); if (this.scanRecord == null && this.legacyScanRecord == null) - throw new IllegalStateException("ScanRecord required. Builder#scanRecord should be called."); - RxBleDeviceMock rxBleDeviceMock; + throw new IllegalStateException("ScanRecord required. DeviceBuilder#scanRecord should be called."); + + RxBleConnectionMock connMock = connectionMock == null ? connectionMockBuilder.build() : connectionMock; + // legacy if (scanRecord == null) { - rxBleDeviceMock = new RxBleDeviceMock(deviceName, + RxBleDeviceMock rxBleDeviceMock = new RxBleDeviceMock(deviceName, deviceMacAddress, legacyScanRecord, - rssi, - rxBleDeviceServices, - characteristicNotificationSources, - bluetoothDevice); - } else { - rxBleDeviceMock = new RxBleDeviceMock(deviceName, - deviceMacAddress, - scanRecord, - rssi, - rxBleDeviceServices, - characteristicNotificationSources, + connMock.getRssi(), + connMock.getRxBleDeviceServices(), + connMock.getCharacteristicNotificationSources(), bluetoothDevice); + for (UUID service : connMock.getServiceUuids()) { + rxBleDeviceMock.addAdvertisedUUID(service); + } + return rxBleDeviceMock; } + return new RxBleDeviceMock(deviceName, + deviceMacAddress, + scanRecord, + bluetoothDevice, + connMock + ); + } - for (BluetoothGattService service : rxBleDeviceServices.getBluetoothGattServices()) { - rxBleDeviceMock.addAdvertisedUUID(service.getUuid()); - } - return rxBleDeviceMock; + /** + * Add a {@link BluetoothGattService} to the device. Calling this method is not required. + * + * @param uuid service UUID + * @param characteristics characteristics that the service should report. Use {@link RxBleClientMock.CharacteristicsBuilder} to + * create them. + * @deprecated Use {@link #connection(RxBleConnectionMock connectionMock)} and + * {@link RxBleConnectionMock.Builder#addService(UUID uuid, List characteristics)} + */ + @Deprecated + public Builder addService(@NonNull UUID uuid, @NonNull List characteristics) { + connectionMockBuilder.addService(uuid, characteristics); + return this; } /** @@ -185,17 +178,22 @@ public Builder bluetoothDevice(@NonNull BluetoothDevice bluetoothDevice) { * * @param characteristicUUID UUID of the characteristic that will be observed for notifications * @param sourceObservable Observable that will be subscribed to in order to receive characteristic change notifications + * @deprecated Use {@link #connectionMock} and + * {@link RxBleConnectionMock.Builder#notificationSource(UUID characteristicUUID, Observable sourceObservable)} */ + @Deprecated public Builder notificationSource(@NonNull UUID characteristicUUID, @NonNull Observable sourceObservable) { - characteristicNotificationSources.put(characteristicUUID, sourceObservable); + connectionMockBuilder.notificationSource(characteristicUUID, sourceObservable); return this; } /** * Set a rssi that will be reported. Calling this method is required. + * @deprecated Use {@link #connectionMock} and {@link RxBleConnectionMock.Builder#rssi(int rssi)} */ + @Deprecated public Builder rssi(int rssi) { - this.rssi = rssi; + connectionMockBuilder.rssi(rssi); return this; } @@ -214,6 +212,11 @@ public Builder scanRecord(@NonNull ScanRecord scanRecord) { this.scanRecord = scanRecord; return this; } + + public Builder connection(@NonNull RxBleConnectionMock connectionMock) { + this.connectionMock = connectionMock; + return this; + } } public void addAdvertisedUUID(UUID advertisedUUID) { diff --git a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockLegacyTest.groovy b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockLegacyTest.groovy new file mode 100644 index 000000000..10be75439 --- /dev/null +++ b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockLegacyTest.groovy @@ -0,0 +1,464 @@ +package com.polidea.rxandroidble2.mockrxandroidble + +import android.os.Build +import android.os.ParcelUuid +import com.polidea.rxandroidble2.RxBleClient +import com.polidea.rxandroidble2.RxBleConnection +import com.polidea.rxandroidble2.scan.ScanFilter +import com.polidea.rxandroidble2.scan.ScanSettings +import hkhc.electricspock.ElectricSpecification +import io.reactivex.Observable +import io.reactivex.subjects.PublishSubject +import org.robolectric.annotation.Config + +@Config(manifest = Config.NONE, constants = BuildConfig, sdk = Build.VERSION_CODES.LOLLIPOP) +public class RxBleClientMockLegacyTest extends ElectricSpecification { + + def serviceUUID = UUID.fromString("00001234-0000-0000-8000-000000000000") + def serviceUUID2 = UUID.fromString("00001235-0000-0000-8000-000000000000") + def characteristicUUID = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fb") + def characteristicNotifiedUUID = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fb") + def characteristicData = "Polidea".getBytes() + def descriptorUUID = UUID.fromString("00001337-0000-1000-8000-00805f9b34fb") + def descriptorData = "Config".getBytes() + def RxBleClient rxBleClient + def PublishSubject characteristicNotificationSubject = PublishSubject.create() + + def createDeviceWithLegacyScanRecord(deviceName, macAddress, rssi) { + new RxBleClientMock.DeviceBuilder() + .deviceMacAddress(macAddress) + .deviceName(deviceName) + .scanRecord("ScanRecord".getBytes()) + .rssi(rssi) + .notificationSource(characteristicNotifiedUUID, characteristicNotificationSubject) + .addService( + serviceUUID, + new RxBleClientMock.CharacteristicsBuilder() + .addCharacteristic( + characteristicUUID, + characteristicData, + new RxBleClientMock.DescriptorsBuilder() + .addDescriptor(descriptorUUID, descriptorData) + .build() + ).build() + ).build() + } + + def createDevice(deviceName, macAddress, rssi) { + new RxBleClientMock.DeviceBuilder() + .deviceMacAddress(macAddress) + .deviceName(deviceName) + .scanRecord( + new RxBleScanRecordMock.Builder() + .setAdvertiseFlags(1) + .addServiceUuid(new ParcelUuid(serviceUUID)) + .addServiceUuid(new ParcelUuid(serviceUUID2)) + .addManufacturerSpecificData(0x2211, [0x33, 0x44] as byte[]) + .addServiceData(new ParcelUuid(serviceUUID), [0x11, 0x22] as byte[]) + .setTxPowerLevel(12) + .setDeviceName("TestDeviceAdv") + .build() + ) + .rssi(rssi) + .notificationSource(characteristicNotifiedUUID, characteristicNotificationSubject) + .addService( + serviceUUID, + new RxBleClientMock.CharacteristicsBuilder() + .addCharacteristic( + characteristicUUID, + characteristicData, + new RxBleClientMock.DescriptorsBuilder() + .addDescriptor(descriptorUUID, descriptorData) + .build() + ).build() + ).build() + } + + def setup() { + rxBleClient = new RxBleClientMock.Builder() + .addDevice( + createDevice("TestDevice", "AA:BB:CC:DD:EE:FF", 42) + ).build() + } + + def "should return filtered BluetoothDevice with legacy filter"() { + when: + rxBleClient = new RxBleClientMock.Builder() + .addDevice( + createDeviceWithLegacyScanRecord("TestDevice", "AA:BB:CC:DD:EE:FF", 42) + ).build() + def testSubscriber = rxBleClient.scanBleDevices(serviceUUID) + .take(1) + .map { scanResult -> scanResult.getBleDevice().getMacAddress() } + .test() + + then: + testSubscriber.assertValue("AA:BB:CC:DD:EE:FF") + } + + def "should return filtered BluetoothDevice filtered on service UUID"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUUID)).build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .take(1) + .map { scanResult -> scanResult.getBleDevice().getMacAddress() } + .test() + + then: + testSubscriber.assertValue("AA:BB:CC:DD:EE:FF") + } + + def "should return filtered BluetoothDevice filtered on service UUID only in scan record"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUUID2)).build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .take(1) + .map { scanResult -> scanResult.getBleDevice().getMacAddress() } + .test() + + then: + testSubscriber.assertValue("AA:BB:CC:DD:EE:FF") + } + + def "should not return filtered BluetoothDevice filtered on invalid service UUID"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setServiceUuid( + new ParcelUuid(UUID.fromString("00001236-0000-0000-8000-000000000000")) + ).build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .test() + + then: + testSubscriber.assertEmpty() + } + + def "should return filtered BluetoothDevice filtered on masked service UUID"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder() + .setServiceUuid( + new ParcelUuid(UUID.fromString("00001230-0000-0000-8000-000000000000")), + new ParcelUuid(UUID.fromString("0000FFF0-0000-0000-8000-000000000000")) + ) + .build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .take(1) + .map { scanResult -> scanResult.getBleDevice().getMacAddress() } + .test() + + then: + testSubscriber.assertValue("AA:BB:CC:DD:EE:FF") + } + + def "should not return filtered BluetoothDevice filtered on invalid masked service UUID"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder() + .setServiceUuid( + new ParcelUuid(UUID.fromString("00001200-0000-0000-8000-000000000000")), + new ParcelUuid(UUID.fromString("0000FFF0-0000-0000-8000-000000000000")) + ) + .build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .test() + + then: + testSubscriber.assertEmpty() + } + + def "should return filtered BluetoothDevice filtered on manufacturer data"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setManufacturerData(0x2211, [0x33, 0x44] as byte[]).build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .take(1) + .map { scanResult -> scanResult.getBleDevice().getMacAddress() } + .test() + + then: + testSubscriber.assertValue("AA:BB:CC:DD:EE:FF") + } + + def "should not return filtered BluetoothDevice filtered on invalid manufacturer id"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setManufacturerData(0x2212, [0x33, 0x44] as byte[]).build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .test() + + then: + testSubscriber.assertEmpty() + } + + def "should return filtered BluetoothDevice filtered on invalid manufacturer data"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setManufacturerData(0x2211, [0x33, 0x45] as byte[]).build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .test() + + then: + testSubscriber.assertEmpty() + } + + def "should return filtered BluetoothDevice filtered on masked manufacturer data"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setManufacturerData(0x2211, [0x30, 0x40] as byte[], [0xF0, 0xF0] as byte[]).build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .take(1) + .map { scanResult -> scanResult.getBleDevice().getMacAddress() } + .test() + + then: + testSubscriber.assertValue("AA:BB:CC:DD:EE:FF") + } + + def "should not return filtered BluetoothDevice filtered on masked manufacturer data"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setManufacturerData(0x2211, [0x30, 0x40] as byte[], [0xFF, 0xFF] as byte[]).build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .test() + + then: + testSubscriber.assertEmpty() + } + + def "should return filtered BluetoothDevice filtered on service data"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setServiceData(new ParcelUuid(serviceUUID), [0x11, 0x22] as byte[]).build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .take(1) + .map { scanResult -> scanResult.getBleDevice().getMacAddress() } + .test() + + then: + testSubscriber.assertValue("AA:BB:CC:DD:EE:FF") + } + + def "should not return filtered BluetoothDevice filtered on service data"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setServiceData(new ParcelUuid(serviceUUID), [0x11, 0x33] as byte[]).build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .test() + + then: + testSubscriber.assertEmpty() + } + + def "should return filtered BluetoothDevice filtered on masked service data"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setServiceData(new ParcelUuid(serviceUUID), [0x10, 0x20] as byte[], [0xF0, 0xF0] as byte[]).build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .take(1) + .map { scanResult -> scanResult.getBleDevice().getMacAddress() } + .test() + + then: + testSubscriber.assertValue("AA:BB:CC:DD:EE:FF") + } + + def "should not return filtered BluetoothDevice filtered on masked service data"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setServiceData(new ParcelUuid(serviceUUID), [0x10, 0x20] as byte[], [0xFF, 0xFF] as byte[]).build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .test() + + then: + testSubscriber.assertEmpty() + } + + def "should return filtered BluetoothDevice filtered on device name"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setDeviceName("TestDeviceAdv").build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .take(1) + .map { scanResult -> scanResult.getBleDevice().getMacAddress() } + .test() + + then: + testSubscriber.assertValue("AA:BB:CC:DD:EE:FF") + } + + def "should not return filtered BluetoothDevice filtered on device name"() { + when: + def scanSettings = new ScanSettings.Builder().build() + def scanFilter = new ScanFilter.Builder().setDeviceName("TestDeviceAdvv").build() + def testSubscriber = rxBleClient.scanBleDevices(scanSettings, scanFilter) + .test() + + then: + testSubscriber.assertEmpty() + } + + def "should return the BluetoothDevice name"() { + when: + def testSubscriber = rxBleClient.scanBleDevices() + .take(1) + .map { scanResult -> scanResult.getBleDevice().getName() } + .test() + + then: + testSubscriber.assertValue("TestDevice") + } + + def "should return the BluetoothDevice address"() { + when: + def testSubscriber = rxBleClient.scanBleDevices() + .take(1) + .map { scanResult -> scanResult.getBleDevice().getMacAddress() } + .test() + + then: + testSubscriber.assertValue("AA:BB:CC:DD:EE:FF") + } + + def "should return the BluetoothDevice rssi"() { + when: + def testSubscriber = rxBleClient.scanBleDevices() + .take(1) + .map { scanResult -> scanResult.getRssi() } + .test() + + then: + testSubscriber.assertValue(42) + } + + def "should return the BluetoothDevice mtu"() { + when: + def testSubscriber = rxBleClient.scanBleDevices(null) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapSingle { rxBleConnection -> rxBleConnection.requestMtu(72) } + .test() + + then: + testSubscriber.assertValue(72) + } + + def "should return BluetoothDevices that were added on the fly"() { + given: + def discoverableDevicesSubject = PublishSubject.create() + def dynRxBleClient = new RxBleClientMock.Builder() + .setDeviceDiscoveryObservable(discoverableDevicesSubject) + .build() + discoverableDevicesSubject.onNext(createDevice("TestDevice", "AA:BB:CC:DD:EE:FF", 42)) + discoverableDevicesSubject.onNext(createDevice("SecondDevice", "AA:BB:CC:DD:EE:00", 17)) + + when: + def scanObservable = dynRxBleClient.scanBleDevices() + def testNameSubscriber = scanObservable.map { scanResult -> scanResult.getBleDevice().getName() }.test() + def testAddressSubscriber = scanObservable.map { scanResult -> scanResult.getBleDevice().getMacAddress() }.test() + def testRssiSubscriber = scanObservable.map { scanResult -> scanResult.getRssi() }.test() + + then: + testNameSubscriber.assertValues("TestDevice", "SecondDevice") + testAddressSubscriber.assertValues("AA:BB:CC:DD:EE:FF", "AA:BB:CC:DD:EE:00") + testRssiSubscriber.assertValues(42, 17) + } + + def "should return services list"() { + when: + def testSubscriber = rxBleClient.scanBleDevices(null) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapSingle { rxBleConnection -> + rxBleConnection.discoverServices() + .map { rxBleDeviceServices -> rxBleDeviceServices.getBluetoothGattServices() } + .map { servicesList -> servicesList.size() } + } + .test() + + then: + testSubscriber.assertValue(1) + } + + def "should return characteristic data"() { + when: + def testSubscriber = rxBleClient.scanBleDevices(null) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapSingle { rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID) } + .map { data -> new String(data) } + .test() + + then: + testSubscriber.assertValue("Polidea") + } + + def "should return descriptor data"() { + when: + def testSubscriber = rxBleClient.scanBleDevices(null) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapSingle { rxBleConnection -> rxBleConnection.readDescriptor(serviceUUID, characteristicUUID, descriptorUUID) } + .map { data -> new String(data) } + .test() + + then: + testSubscriber.assertValue("Config") + } + + def "should return notification data"() { + given: + def testSubscriber = rxBleClient.scanBleDevices(null) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMap { rxBleConnection -> rxBleConnection.setupNotification(characteristicNotifiedUUID) } + .flatMap({ Observable observable -> observable }) + .map { new String(it) } + .test() + + when: + characteristicNotificationSubject.onNext("NotificationData".getBytes()) + + then: + testSubscriber.assertValue("NotificationData") + } + + def "should emit correct connection state values when connected"() { + given: + def device = rxBleClient.getBleDevice("AA:BB:CC:DD:EE:FF") + def testSubscriber = device.observeConnectionStateChanges().test() + + when: + device.establishConnection(false).subscribe {} + + then: + testSubscriber.assertValues( + RxBleConnection.RxBleConnectionState.DISCONNECTED, + RxBleConnection.RxBleConnectionState.CONNECTING, + RxBleConnection.RxBleConnectionState.CONNECTED) + } + + def "should emit correct connection state values when disconnected"() { + given: + def device = rxBleClient.getBleDevice("AA:BB:CC:DD:EE:FF") + def testSubscriber = device.observeConnectionStateChanges().test() + def subscription = device.establishConnection(false).test() + + when: + subscription.dispose() + + then: + testSubscriber.assertValues( + RxBleConnection.RxBleConnectionState.DISCONNECTED, + RxBleConnection.RxBleConnectionState.CONNECTING, + RxBleConnection.RxBleConnectionState.CONNECTED, + RxBleConnection.RxBleConnectionState.DISCONNECTED) + } +} diff --git a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy index 69cdd13bd..0d54023b6 100644 --- a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy +++ b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy @@ -24,28 +24,8 @@ public class RxBleClientMockTest extends ElectricSpecification { def RxBleClient rxBleClient def PublishSubject characteristicNotificationSubject = PublishSubject.create() - def createDeviceWithLegacyScanRecord(deviceName, macAddress, rssi) { - new RxBleClientMock.DeviceBuilder() - .deviceMacAddress(macAddress) - .deviceName(deviceName) - .scanRecord("ScanRecord".getBytes()) - .rssi(rssi) - .notificationSource(characteristicNotifiedUUID, characteristicNotificationSubject) - .addService( - serviceUUID, - new RxBleClientMock.CharacteristicsBuilder() - .addCharacteristic( - characteristicUUID, - characteristicData, - new RxBleClientMock.DescriptorsBuilder() - .addDescriptor(descriptorUUID, descriptorData) - .build() - ).build() - ).build() - } - def createDevice(deviceName, macAddress, rssi) { - new RxBleClientMock.DeviceBuilder() + new RxBleDeviceMock.Builder() .deviceMacAddress(macAddress) .deviceName(deviceName) .scanRecord( @@ -59,18 +39,19 @@ public class RxBleClientMockTest extends ElectricSpecification { .setDeviceName("TestDeviceAdv") .build() ) - .rssi(rssi) - .notificationSource(characteristicNotifiedUUID, characteristicNotificationSubject) - .addService( - serviceUUID, - new RxBleClientMock.CharacteristicsBuilder() - .addCharacteristic( - characteristicUUID, - characteristicData, - new RxBleClientMock.DescriptorsBuilder() - .addDescriptor(descriptorUUID, descriptorData) - .build() - ).build() + .connection(new RxBleConnectionMock.Builder() + .rssi(rssi) + .notificationSource(characteristicNotifiedUUID, characteristicNotificationSubject) + .addService(serviceUUID, + new RxBleClientMock.CharacteristicsBuilder() + .addCharacteristic( + characteristicUUID, + characteristicData, + new RxBleClientMock.DescriptorsBuilder() + .addDescriptor(descriptorUUID, descriptorData) + .build() + ).build() + ).build() ).build() } @@ -81,21 +62,6 @@ public class RxBleClientMockTest extends ElectricSpecification { ).build() } - def "should return filtered BluetoothDevice with legacy filter"() { - when: - rxBleClient = new RxBleClientMock.Builder() - .addDevice( - createDeviceWithLegacyScanRecord("TestDevice", "AA:BB:CC:DD:EE:FF", 42) - ).build() - def testSubscriber = rxBleClient.scanBleDevices(serviceUUID) - .take(1) - .map { scanResult -> scanResult.getBleDevice().getMacAddress() } - .test() - - then: - testSubscriber.assertValue("AA:BB:CC:DD:EE:FF") - } - def "should return filtered BluetoothDevice filtered on service UUID"() { when: def scanSettings = new ScanSettings.Builder().build() From 0568cc6e5671b3185afdc1114d473dcf57be6a1f Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Fri, 24 Jul 2020 11:51:25 +0100 Subject: [PATCH 04/21] Readme updates --- mockrxandroidble/README.md | 45 ++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/mockrxandroidble/README.md b/mockrxandroidble/README.md index 5362e19d8..0ab06fc31 100644 --- a/mockrxandroidble/README.md +++ b/mockrxandroidble/README.md @@ -12,30 +12,33 @@ Here's an example: ```java RxBleClient rxBleClientMock = new RxBleClientMock.Builder() - .addDevice(new RxBleClientMock.DeviceBuilder() // <-- creating device mock, there can me multiple of them + .addDevice(new RxBleDeviceMock.Builder() // <-- creating device mock, there can me multiple of them .deviceMacAddress(macAddress) .deviceName(deviceName) - .scanRecord( - new RxBleScanRecordMock.Builder() - .setAdvertiseFlags(1) - .addServiceUuid(new ParcelUuid(serviceUUID)) - .addServiceUuid(new ParcelUuid(serviceUUID2)) - .addManufacturerSpecificData(0x2211, [0x33, 0x44] as byte[]) - .addServiceData(new ParcelUuid(serviceUUID), [0x11, 0x22] as byte[]) - .setTxPowerLevel(12) - .setDeviceName("TestDeviceAdv") - .build() + .scanRecord(new RxBleScanRecordMock.Builder() + .setAdvertiseFlags(1) + .addServiceUuid(new ParcelUuid(serviceUUID)) + .addServiceUuid(new ParcelUuid(serviceUUID2)) + .addManufacturerSpecificData(0x2211, [0x33, 0x44] as byte[]) + .addServiceData(new ParcelUuid(serviceUUID), [0x11, 0x22] as byte[]) + .setTxPowerLevel(12) + .setDeviceName("TestDeviceAdv") + .build() ) - .rssi(rssiValue) - .addService( // <-- adding service mocks to the device, there can be multiple of them - serviceUUID, - new RxBleClientMock.CharacteristicsBuilder() - .addCharacteristic( // <-- adding characteristic mocks to the service, there can be multiple of them - characteristicUUID, - characteristicDataBytes, - new RxBleClientMock.DescriptorsBuilder() - .addDescriptor(descriptorUUID, descriptorDataBytes) // <-- adding descriptor mocks - .build() // to the characteristic, there can be multiple of them + .connection(new RxBleConnectionMock.Builder() // <-- creating connection mock + .rssi(rssi) + .notificationSource(characteristicNotifiedUUID, characteristicNotificationSubject) + .addService( // <-- adding service mocks to the device, there can be multiple of them + serviceUUID, + new RxBleClientMock.CharacteristicsBuilder() + .addCharacteristic( // <-- adding characteristic mocks to the service, there can be multiple of them + characteristicUUID, + characteristicData, + new RxBleClientMock.DescriptorsBuilder() + .addDescriptor(descriptorUUID, descriptorData) // <-- adding descriptor mocks + .build() // to the characteristic, there can be multiple of them + ).build() + ).build() ).build() ).build(); From b7b9d5920c48b86025a5e618b853f80668ca9cf5 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Fri, 24 Jul 2020 17:21:47 +0100 Subject: [PATCH 05/21] Updated tests using scanBleDevices to use new method --- .../mockrxandroidble/RxBleClientMockTest.groovy | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy index 0d54023b6..636596875 100644 --- a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy +++ b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy @@ -301,7 +301,7 @@ public class RxBleClientMockTest extends ElectricSpecification { def "should return the BluetoothDevice mtu"() { when: - def testSubscriber = rxBleClient.scanBleDevices(null) + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } @@ -335,7 +335,7 @@ public class RxBleClientMockTest extends ElectricSpecification { def "should return services list"() { when: - def testSubscriber = rxBleClient.scanBleDevices(null) + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } @@ -352,7 +352,7 @@ public class RxBleClientMockTest extends ElectricSpecification { def "should return characteristic data"() { when: - def testSubscriber = rxBleClient.scanBleDevices(null) + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } @@ -366,7 +366,7 @@ public class RxBleClientMockTest extends ElectricSpecification { def "should return descriptor data"() { when: - def testSubscriber = rxBleClient.scanBleDevices(null) + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } @@ -380,7 +380,7 @@ public class RxBleClientMockTest extends ElectricSpecification { def "should return notification data"() { given: - def testSubscriber = rxBleClient.scanBleDevices(null) + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } From 603336ba791b91a54648a0a6750497282b9df488 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Fri, 24 Jul 2020 17:24:17 +0100 Subject: [PATCH 06/21] Merge fixes --- .../mockrxandroidble/RxBleClientMock.java | 3 - .../mockrxandroidble/RxBleConnectionMock.java | 75 ++++++++++++++++++- .../mockrxandroidble/RxBleDeviceMock.java | 27 ------- 3 files changed, 74 insertions(+), 31 deletions(-) diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java index d3de382c0..fc5a6ecd1 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMock.java @@ -24,10 +24,7 @@ import java.util.Set; import java.util.UUID; -import io.reactivex.Completable; import io.reactivex.Observable; -import io.reactivex.Single; -import io.reactivex.functions.BiFunction; import io.reactivex.functions.Function; import io.reactivex.functions.Predicate; import io.reactivex.subjects.ReplaySubject; diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java index 721274927..67d0d85e5 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java @@ -656,6 +656,10 @@ public static class Builder { private RxBleDeviceServices rxBleDeviceServices; private int rssi; private Map> characteristicNotificationSources = new HashMap<>(); + private Map>> characteristicReadCallbacks = new HashMap<>(); + private Map> characteristicWriteCallbacks = new HashMap<>(); + private Map>>> descriptorReadCallbacks = new HashMap<>(); + private Map>> descriptorWriteCallbacks = new HashMap<>(); public Builder() { @@ -666,7 +670,11 @@ public RxBleConnectionMock build() { return new RxBleConnectionMock( rxBleDeviceServices, rssi, - characteristicNotificationSources + characteristicNotificationSources, + characteristicReadCallbacks, + characteristicWriteCallbacks, + descriptorReadCallbacks, + descriptorWriteCallbacks ); } @@ -706,5 +714,70 @@ public Builder notificationSource(@NonNull UUID characteristicUUID, @NonNull Obs characteristicNotificationSources.put(characteristicUUID, sourceObservable); return this; } + + /** + * Set a {@link Function} that will be used to handle characteristic reads for characteristics with a given UUID. The + * function should return a Single which will emit the read data when complete. Calling this method is not required. + * @param characteristicUUID UUID of the characteristic that the callback will handle reads for + * @param readCallback The callback + */ + public Builder characteristicReadCallback(@NonNull UUID characteristicUUID, + @NonNull Function> readCallback) { + characteristicReadCallbacks.put(characteristicUUID, readCallback); + return this; + } + + /** + * Set a {@link Function} that will be used to handle characteristic writes for characteristics with a given UUID. The + * function should return a Completable that completes when the write completes. Calling this method is not required. + * @param characteristicUUID UUID of the characteristic that the callback will handle reads for + * @param writeCallback The callback + */ + public Builder characteristicWriteCallback(@NonNull UUID characteristicUUID, + @NonNull BiFunction writeCallback) { + characteristicWriteCallbacks.put(characteristicUUID, writeCallback); + return this; + } + + /** + * Set a {@link Function} that will be used to handle descriptor reads for descriptors with a given UUID. The + * function should return a Single which will emit the read data when complete. Calling this method is not required. + * @param characteristicUUID UUID of the characteristic that the descriptor is used in + * @param descriptorUUID UUID of the descriptor that the callback will handle reads for + * @param readCallback The callback + */ + public Builder descriptorReadCallback(@NonNull UUID characteristicUUID, + @NonNull UUID descriptorUUID, + @NonNull Function> readCallback) { + Map>> descriptorCallbacks = descriptorReadCallbacks + .get(characteristicUUID); + if (descriptorCallbacks == null) { + descriptorCallbacks = new HashMap<>(); + descriptorReadCallbacks.put(characteristicUUID, descriptorCallbacks); + } + descriptorCallbacks.put(descriptorUUID, readCallback); + return this; + } + + /** + * Set a {@link Function} that will be used to handle descriptor writes for descriptors with a given UUID. The + * function should return a Completable that completes when the write completes. Calling this method is not required. + * @param characteristicUUID UUID of the characteristic that the descriptor is used in + * @param descriptorUUID UUID of the descriptor that the callback will handle reads for + * @param writeCallback The callback + */ + public Builder descriptorWriteCallback(@NonNull UUID characteristicUUID, + @NonNull UUID descriptorUUID, + @NonNull BiFunction writeCallback) { + Map> descriptorCallbacks = descriptorWriteCallbacks + .get(characteristicUUID); + if (descriptorCallbacks == null) { + descriptorCallbacks = new HashMap<>(); + descriptorWriteCallbacks.put(characteristicUUID, descriptorCallbacks); + } + descriptorCallbacks.put(descriptorUUID, writeCallback); + return this; + } } } diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java index f458262a4..86088822e 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java @@ -89,33 +89,6 @@ public RxBleDeviceMock(String name, this.bluetoothDevice = bluetoothDevice; } - public RxBleDeviceMock(String name, - String macAddress, - ScanRecord scanRecord, - RxBleDeviceServices rxBleDeviceServices, - Map> characteristicNotificationSources, - Map>> characteristicReadCallbacks, - Map> characteristicWriteCallbacks, - Map>>> descriptorReadCallbacks, - Map>> descriptorWriteCallbacks, - @Nullable BluetoothDevice bluetoothDevice) { - this( - name, - macAddress, - scanRecord, - bluetoothDevice, - new RxBleConnectionMock( - rxBleDeviceServices, - rssi, - characteristicNotificationSources, - characteristicReadCallbacks, - characteristicWriteCallbacks, - descriptorReadCallbacks, - descriptorWriteCallbacks - ) - ); - } - public static class Builder { private String deviceName; From a1fd4fc629153bbf6f5db6faa2309bd371453c21 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Fri, 24 Jul 2020 17:24:32 +0100 Subject: [PATCH 07/21] Added tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1 failing, not sure why… --- .../RxBleClientMockTest.groovy | 207 +++++++++++++++++- 1 file changed, 202 insertions(+), 5 deletions(-) diff --git a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy index 636596875..b5e230a4f 100644 --- a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy +++ b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy @@ -4,10 +4,15 @@ import android.os.Build import android.os.ParcelUuid import com.polidea.rxandroidble2.RxBleClient import com.polidea.rxandroidble2.RxBleConnection +import com.polidea.rxandroidble2.exceptions.BleGattCharacteristicException +import com.polidea.rxandroidble2.exceptions.BleGattDescriptorException +import com.polidea.rxandroidble2.exceptions.BleGattOperationType import com.polidea.rxandroidble2.scan.ScanFilter; import com.polidea.rxandroidble2.scan.ScanSettings; import hkhc.electricspock.ElectricSpecification +import io.reactivex.Completable import io.reactivex.Observable +import io.reactivex.Single import io.reactivex.subjects.PublishSubject import org.robolectric.annotation.Config @@ -17,12 +22,36 @@ public class RxBleClientMockTest extends ElectricSpecification { def serviceUUID = UUID.fromString("00001234-0000-0000-8000-000000000000") def serviceUUID2 = UUID.fromString("00001235-0000-0000-8000-000000000000") def characteristicUUID = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fb") + def characteristicUUIDNoCallback = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fc") def characteristicNotifiedUUID = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fb") def characteristicData = "Polidea".getBytes() def descriptorUUID = UUID.fromString("00001337-0000-1000-8000-00805f9b34fb") def descriptorData = "Config".getBytes() - def RxBleClient rxBleClient - def PublishSubject characteristicNotificationSubject = PublishSubject.create() + RxBleClient rxBleClient + PublishSubject characteristicNotificationSubject = PublishSubject.create() + + PublishSubject characteristicReadSubject = PublishSubject.create() + PublishSubject characteristicWriteSubject = PublishSubject.create() + PublishSubject descriptorReadSubject = PublishSubject.create() + PublishSubject descriptorWriteSubject = PublishSubject.create() + + Single processReadSubject(PublishSubject subject) { + subject.take(1).singleOrError() + } + + Completable processWriteSubject(PublishSubject subject, BleGattOperationType type) { + subject.take(1).flatMapCompletable({ shouldSucceed -> + if(!shouldSucceed) { + if(type == BleGattOperationType.CHARACTERISTIC_WRITE) { + throw new BleGattCharacteristicException(null, null, 0x80, type); + } else { + throw new BleGattDescriptorException(null, null, 0x80, type); + } + } + Completable.complete(); + }) + } + def createDevice(deviceName, macAddress, rssi) { new RxBleDeviceMock.Builder() @@ -50,8 +79,27 @@ public class RxBleClientMockTest extends ElectricSpecification { new RxBleClientMock.DescriptorsBuilder() .addDescriptor(descriptorUUID, descriptorData) .build() + ).addCharacteristic( + characteristicUUIDNoCallback, + characteristicData, + new RxBleClientMock.DescriptorsBuilder() + .addDescriptor(descriptorUUID, descriptorData) + .build() ).build() - ).build() + ) + .characteristicReadCallback(characteristicUUID, {characteristic -> + processReadSubject(characteristicReadSubject) + }) + .characteristicWriteCallback(characteristicUUID, { characteristic, bytes -> + processWriteSubject(characteristicWriteSubject, BleGattOperationType.CHARACTERISTIC_WRITE) + }) + .descriptorReadCallback(characteristicUUID, descriptorUUID, { descriptor -> + processReadSubject(descriptorReadSubject) + }) + .descriptorWriteCallback(characteristicUUID, descriptorUUID, { descriptor, bytes -> + processWriteSubject(descriptorWriteSubject, BleGattOperationType.DESCRIPTOR_WRITE) + }) + .build() ).build() } @@ -356,7 +404,7 @@ public class RxBleClientMockTest extends ElectricSpecification { .take(1) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID) } + .flatMapSingle { rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUIDNoCallback) } .map { data -> new String(data) } .test() @@ -364,13 +412,87 @@ public class RxBleClientMockTest extends ElectricSpecification { testSubscriber.assertValue("Polidea") } + def "should return characteristic data via callback"() { + given: + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapSingle { rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID) } + .map { data -> new String(data) } + .test() + + when: + characteristicReadSubject.onNext("PolideaCB".getBytes()) + + then: + testSubscriber.assertValue("PolideaCB") + } + + def "should throw characteristic read error via callback"() { + given: + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapSingle { rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID) } + .map { data -> new String(data) } + .test() + + when: + characteristicReadSubject.onError(new Throwable("read error")) + + then: + testSubscriber.assertErrorMessage("read error") + } + + def "should write characteristic data via callback"() { + given: + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapSingle { rxBleConnection -> rxBleConnection.writeCharacteristic(characteristicUUID, "PolideaCB".getBytes()) } + .map { data -> new String(data) } + .test() + + when: + characteristicWriteSubject.onNext(Boolean.TRUE); + + then: + testSubscriber.assertValue("PolideaCB") + } + + def "should fail to write characteristic data via callback"() { + given: + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapSingle { rxBleConnection -> rxBleConnection.writeCharacteristic(characteristicUUID, "PolideaCB".getBytes()) } + .map { data -> new String(data) } + .test() + + when: + characteristicWriteSubject.onNext(Boolean.FALSE); + + then: + testSubscriber.assertError({ throwable -> + if (!BleGattCharacteristicException.class.isInstance(throwable)) { + return false; + } + BleGattCharacteristicException e = (BleGattCharacteristicException) throwable; + return e.getStatus() == 0x80 + }) + } + def "should return descriptor data"() { when: def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.readDescriptor(serviceUUID, characteristicUUID, descriptorUUID) } + .flatMapSingle { rxBleConnection -> rxBleConnection.readDescriptor(serviceUUID, characteristicUUIDNoCallback, descriptorUUID) } .map { data -> new String(data) } .test() @@ -378,6 +500,81 @@ public class RxBleClientMockTest extends ElectricSpecification { testSubscriber.assertValue("Config") } + def "should return descriptor data via callback"() { + given: + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapSingle { rxBleConnection -> rxBleConnection.readDescriptor(serviceUUID, characteristicUUID, descriptorUUID) } + .map { data -> new String(data) } + .test() + + when: + descriptorReadSubject.onNext("ConfigCB".getBytes()) + + then: + testSubscriber.assertValue("ConfigCB") + } + + def "should throw descriptor read error via callback"() { + given: + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapSingle { rxBleConnection -> rxBleConnection.readDescriptor(serviceUUID, characteristicUUID, descriptorUUID) } + .map { data -> new String(data) } + .test() + + when: + descriptorReadSubject.onError(new Throwable("read error")) + + then: + testSubscriber.assertErrorMessage("read error") + } + + def "should write descriptor data via callback"() { + given: + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapCompletable { rxBleConnection -> rxBleConnection.writeDescriptor(serviceUUID, characteristicUUID, descriptorUUID, "ConfigCB".getBytes()) } + .doOnComplete({ + printf("Test") + }) + .test() + + when: + descriptorWriteSubject.onNext(Boolean.TRUE); + + then: + testSubscriber.assertComplete(); + } + + def "should fail to write descriptor data via callback"() { + given: + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapCompletable { rxBleConnection -> rxBleConnection.writeDescriptor(serviceUUID, characteristicUUID, descriptorUUID, "ConfigCB".getBytes()) } + .test() + + when: + descriptorWriteSubject.onNext(Boolean.FALSE); + + then: + testSubscriber.assertError({ throwable -> + if (!BleGattDescriptorException.class.isInstance(throwable)) { + return false; + } + BleGattDescriptorException e = (BleGattDescriptorException) throwable; + return e.getStatus() == 0x80 + }) + } + def "should return notification data"() { given: def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) From 5910089a8b4048b7a234db99bb52a4af82680faa Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Fri, 24 Jul 2020 17:41:18 +0100 Subject: [PATCH 08/21] Removed unused imports --- .../rxandroidble2/mockrxandroidble/RxBleConnectionMock.java | 1 - .../polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java | 1 - 2 files changed, 2 deletions(-) diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java index 3d42ef15e..8a0bfcd84 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java @@ -33,7 +33,6 @@ import io.reactivex.SingleSource; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Action; -import io.reactivex.functions.BiFunction; import io.reactivex.functions.Consumer; import io.reactivex.functions.Function; import io.reactivex.functions.Predicate; diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java index 967a44e10..899ff7d0b 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java @@ -15,7 +15,6 @@ import com.polidea.rxandroidble2.scan.ScanRecord; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; From ebdc6bbe5efc31fefdb4857b1ae6e787053bf895 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Fri, 24 Jul 2020 17:54:48 +0100 Subject: [PATCH 09/21] Added missing imports --- .../rxandroidble2/mockrxandroidble/RxBleConnectionMock.java | 1 + .../polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java | 1 + 2 files changed, 2 insertions(+) diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java index 5cf4f04d5..67d0d85e5 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java @@ -33,6 +33,7 @@ import io.reactivex.SingleSource; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Action; +import io.reactivex.functions.BiFunction; import io.reactivex.functions.Consumer; import io.reactivex.functions.Function; import io.reactivex.functions.Predicate; diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java index 61721097f..86088822e 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java @@ -16,6 +16,7 @@ import com.polidea.rxandroidble2.scan.ScanRecord; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; From 16aa351a7e0e6814da7e5dc475487f0e7575f03d Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Fri, 24 Jul 2020 17:59:22 +0100 Subject: [PATCH 10/21] Added callbacks to mock readme --- mockrxandroidble/README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/mockrxandroidble/README.md b/mockrxandroidble/README.md index 0ab06fc31..58346dfc1 100644 --- a/mockrxandroidble/README.md +++ b/mockrxandroidble/README.md @@ -38,7 +38,22 @@ RxBleClient rxBleClientMock = new RxBleClientMock.Builder() .addDescriptor(descriptorUUID, descriptorData) // <-- adding descriptor mocks .build() // to the characteristic, there can be multiple of them ).build() - ).build() + ).characteristicReadCallback(characteristicUUID, new Function>() { + @Override + public Single apply(BluetoothGattCharacteristic characteristic) throws Exception { + return Single.just(characteristicValue); + } + }) + .characteristicWriteCallback(characteristicUUID, new BiFunction() { + @Override + public Completable apply(BluetoothGattCharacteristic characteristic, byte[] bytes) throws Exception { + if(!writeData(characteristic, bytes)) { + throw new BleGattCharacteristicException(null, characteristic, 0x80, BleGattOperationType.CHARACTERISTIC_WRITE); + + } + return Completable.complete(); + } + }).build() ).build() ).build(); From bb9f1344559f592134dc73215fa9fd6c2cc1f1c1 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Fri, 24 Jul 2020 21:01:35 +0100 Subject: [PATCH 11/21] Fixed test --- .../mockrxandroidble/RxBleClientMockTest.groovy | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy index b5e230a4f..ce560c98c 100644 --- a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy +++ b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy @@ -537,13 +537,10 @@ public class RxBleClientMockTest extends ElectricSpecification { def "should write descriptor data via callback"() { given: def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .take(1) .flatMapCompletable { rxBleConnection -> rxBleConnection.writeDescriptor(serviceUUID, characteristicUUID, descriptorUUID, "ConfigCB".getBytes()) } - .doOnComplete({ - printf("Test") - }) .test() when: @@ -556,9 +553,9 @@ public class RxBleClientMockTest extends ElectricSpecification { def "should fail to write descriptor data via callback"() { given: def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .take(1) .flatMapCompletable { rxBleConnection -> rxBleConnection.writeDescriptor(serviceUUID, characteristicUUID, descriptorUUID, "ConfigCB".getBytes()) } .test() From 42df28cf30ed37e93323e62da4fc96b8986157a4 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Thu, 30 Jul 2020 11:39:56 +0100 Subject: [PATCH 12/21] Switch to lambdas in mock docs --- mockrxandroidble/README.md | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/mockrxandroidble/README.md b/mockrxandroidble/README.md index 58346dfc1..a802f6f53 100644 --- a/mockrxandroidble/README.md +++ b/mockrxandroidble/README.md @@ -38,21 +38,15 @@ RxBleClient rxBleClientMock = new RxBleClientMock.Builder() .addDescriptor(descriptorUUID, descriptorData) // <-- adding descriptor mocks .build() // to the characteristic, there can be multiple of them ).build() - ).characteristicReadCallback(characteristicUUID, new Function>() { - @Override - public Single apply(BluetoothGattCharacteristic characteristic) throws Exception { - return Single.just(characteristicValue); - } + ).characteristicReadCallback(characteristicUUID, (characteristic) -> { + return Single.just(characteristicValue); }) - .characteristicWriteCallback(characteristicUUID, new BiFunction() { - @Override - public Completable apply(BluetoothGattCharacteristic characteristic, byte[] bytes) throws Exception { - if(!writeData(characteristic, bytes)) { - throw new BleGattCharacteristicException(null, characteristic, 0x80, BleGattOperationType.CHARACTERISTIC_WRITE); - - } - return Completable.complete(); + .characteristicWriteCallback(characteristicUUID, (characteristic, bytes) -> { + if(!writeData(characteristic, bytes)) { + throw new BleGattCharacteristicException(null, characteristic, 0x80, BleGattOperationType.CHARACTERISTIC_WRITE); + } + return Completable.complete(); }).build() ).build() ).build(); From eaca7accd0c4c435d059ca52b7add5bf86b2ac36 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Thu, 30 Jul 2020 11:57:35 +0100 Subject: [PATCH 13/21] Documentation improvements --- .../rxandroidble2/mockrxandroidble/RxBleDeviceMock.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java index 899ff7d0b..c9c39cd15 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java @@ -197,7 +197,7 @@ public Builder rssi(int rssi) { } /** - * Set a BLE scan record. Calling either this method or the other {@link #scanRecord(ScanRecord) scanRecord} method is required. + * Set a BLE scan record. Calling either this method or the other {@link #scanRecord(ScanRecord)} method is required. */ public Builder scanRecord(@NonNull byte[] scanRecord) { this.legacyScanRecord = scanRecord; @@ -205,13 +205,16 @@ public Builder scanRecord(@NonNull byte[] scanRecord) { } /** - * Set a BLE scan record. Calling this method is required. + * Set a BLE scan record. Calling either this method or the other {@link #scanRecord(byte[])}} method is required. */ public Builder scanRecord(@NonNull ScanRecord scanRecord) { this.scanRecord = scanRecord; return this; } + /** + * Set a BLE connection mock. Calling this method is required. + */ public Builder connection(@NonNull RxBleConnectionMock connectionMock) { this.connectionMock = connectionMock; return this; From 4ce5dd2b781cb6ef0a92584a3ae834ef31ec0922 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Fri, 31 Jul 2020 10:35:24 +0100 Subject: [PATCH 14/21] Removed duplicate implementation --- .../mockrxandroidble/RxBleConnectionMock.java | 131 ++++-------------- 1 file changed, 29 insertions(+), 102 deletions(-) diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java index 67d0d85e5..3e5d2323a 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java @@ -156,25 +156,10 @@ public SingleSource apply(RxBleDeviceServ @Override public Single readCharacteristic(@NonNull UUID characteristicUuid) { - final Function> readCallback = characteristicReadCallbacks.get(characteristicUuid); - Single characteristic = getCharacteristic(characteristicUuid); - if (readCallback == null) { - return characteristic.map(new Function() { - @Override - public byte[] apply(BluetoothGattCharacteristic bluetoothGattCharacteristic) throws Exception { - return bluetoothGattCharacteristic.getValue(); - } - }); - } - return characteristic.flatMap(new Function>() { + return getCharacteristic(characteristicUuid).flatMap(new Function>() { @Override public SingleSource apply(final BluetoothGattCharacteristic characteristic) throws Exception { - return Single.just(characteristic).flatMap(readCallback).doOnSuccess(new Consumer() { - @Override - public void accept(byte[] bytes) throws Exception { - characteristic.setValue(bytes); - } - }); + return readCharacteristic(characteristic); } }); } @@ -196,37 +181,19 @@ public void accept(byte[] bytes) throws Exception { @Override public Single readDescriptor(@NonNull final UUID serviceUuid, @NonNull final UUID characteristicUuid, @NonNull final UUID descriptorUuid) { - final Single descriptor = discoverServices() - .flatMap(new Function>() { - @Override - public SingleSource apply(RxBleDeviceServices rxBleDeviceServices) { - return rxBleDeviceServices.getDescriptor(serviceUuid, characteristicUuid, descriptorUuid); - } - }); - Map>> descriptorCallbacks = descriptorReadCallbacks.get(characteristicUuid); - if (descriptorCallbacks != null) { - final Function> readCallback = descriptorCallbacks.get(descriptorUuid); - if (readCallback != null) { - return descriptor.flatMap(new Function>() { - @Override - public SingleSource apply(final BluetoothGattDescriptor descriptor) throws Exception { - return Single.just(descriptor).flatMap(readCallback).doOnSuccess(new Consumer() { - @Override - public void accept(byte[] bytes) throws Exception { - descriptor.setValue(bytes); - } - }); - } - }); - } - } - return descriptor - .map(new Function() { - @Override - public byte[] apply(BluetoothGattDescriptor bluetoothGattDescriptor) { - return bluetoothGattDescriptor.getValue(); - } - }); + return discoverServices() + .flatMap(new Function>() { + @Override + public SingleSource apply(RxBleDeviceServices rxBleDeviceServices) { + return rxBleDeviceServices.getDescriptor(serviceUuid, characteristicUuid, descriptorUuid); + } + }) + .flatMap(new Function>() { + @Override + public SingleSource apply(final BluetoothGattDescriptor descriptor) throws Exception { + return readDescriptor(descriptor); + } + }); } @Override @@ -379,30 +346,10 @@ public void accept(byte[] bytes) throws Exception { @Override public Single writeCharacteristic(@NonNull UUID characteristicUuid, @NonNull final byte[] data) { - final BiFunction writeCallback = characteristicWriteCallbacks - .get(characteristicUuid); - Single characteristic = getCharacteristic(characteristicUuid); - if (writeCallback == null) { - return characteristic.map(new Function() { - @Override - public byte[] apply(BluetoothGattCharacteristic characteristic) { - characteristic.setValue(data); - return data; - } - }); - } - return characteristic.flatMap(new Function>() { + return getCharacteristic(characteristicUuid).flatMap(new Function>() { @Override public SingleSource apply(final BluetoothGattCharacteristic characteristic) throws Exception { - return writeCallback - .apply(characteristic, data) - .toSingleDefault(data) - .doOnSuccess(new Consumer() { - @Override - public void accept(byte[] bytes) throws Exception { - characteristic.setValue(bytes); - } - }); + return writeCharacteristic(characteristic, data); } }); } @@ -504,38 +451,18 @@ public boolean test(Object object) { @Override public Completable writeDescriptor(@NonNull final UUID serviceUuid, @NonNull final UUID characteristicUuid, @NonNull final UUID descriptorUuid, @NonNull final byte[] data) { - Single descriptor = discoverServices() - .flatMap(new Function>() { - @Override - public SingleSource apply(RxBleDeviceServices rxBleDeviceServices) { - return rxBleDeviceServices.getDescriptor(serviceUuid, characteristicUuid, descriptorUuid); - } - }); - Map> descriptorCallbacks = descriptorWriteCallbacks - .get(characteristicUuid); - if (descriptorCallbacks != null) { - final BiFunction writeCallback = descriptorCallbacks.get(descriptorUuid); - if (writeCallback != null) { - return descriptor.flatMapCompletable(new Function() { - @Override - public Completable apply(final BluetoothGattDescriptor descriptor) throws Exception { - return writeCallback.apply(descriptor, data).doOnComplete(new Action() { - @Override - public void run() throws Exception { - descriptor.setValue(data); - } - }); - } - }); - } - } - return descriptor.doOnSuccess(new Consumer() { - @Override - public void accept(BluetoothGattDescriptor bluetoothGattDescriptor) throws Exception { - bluetoothGattDescriptor.setValue(data); - } - }) - .ignoreElement(); + return discoverServices() + .flatMap(new Function>() { + @Override + public SingleSource apply(RxBleDeviceServices rxBleDeviceServices) { + return rxBleDeviceServices.getDescriptor(serviceUuid, characteristicUuid, descriptorUuid); + } + }).flatMapCompletable(new Function() { + @Override + public Completable apply(final BluetoothGattDescriptor descriptor) throws Exception { + return writeDescriptor(descriptor, data); + } + }); } @Override From 010a09f9942d17ca4e6e9838261117029f680771 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Tue, 11 Aug 2020 16:03:55 +0100 Subject: [PATCH 15/21] Modified mock read+write callbacks to accept device and result parameters --- mockrxandroidble/README.md | 15 +- .../Results/RxBleGattReadResultMock.java | 24 +++ .../Results/RxBleGattWriteResultMock.java | 23 +++ .../RxBleCharacteristicReadCallback.java | 22 +++ .../RxBleCharacteristicWriteCallback.java | 26 +++ .../RxBleDescriptorReadCallback.java | 22 +++ .../RxBleDescriptorWriteCallback.java | 23 +++ .../Callbacks/RxBleReadCallback.java | 21 ++ .../Callbacks/RxBleWriteCallback.java | 26 +++ .../mockrxandroidble/RxBleConnectionMock.java | 183 ++++++++++++++---- .../mockrxandroidble/RxBleDeviceMock.java | 78 +++++--- .../RxBleClientMockTest.groovy | 166 +++++++++------- 12 files changed, 489 insertions(+), 140 deletions(-) create mode 100644 mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattReadResultMock.java create mode 100644 mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattWriteResultMock.java create mode 100644 mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicReadCallback.java create mode 100644 mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicWriteCallback.java create mode 100644 mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorReadCallback.java create mode 100644 mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorWriteCallback.java create mode 100644 mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleReadCallback.java create mode 100644 mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleWriteCallback.java diff --git a/mockrxandroidble/README.md b/mockrxandroidble/README.md index a802f6f53..1395d4694 100644 --- a/mockrxandroidble/README.md +++ b/mockrxandroidble/README.md @@ -38,15 +38,16 @@ RxBleClient rxBleClientMock = new RxBleClientMock.Builder() .addDescriptor(descriptorUUID, descriptorData) // <-- adding descriptor mocks .build() // to the characteristic, there can be multiple of them ).build() - ).characteristicReadCallback(characteristicUUID, (characteristic) -> { - return Single.just(characteristicValue); + ).characteristicReadCallback(characteristicUUID, (device, characteristic, result) -> { + result.success(characteristicValue); }) - .characteristicWriteCallback(characteristicUUID, (characteristic, bytes) -> { - if(!writeData(characteristic, bytes)) { - throw new BleGattCharacteristicException(null, characteristic, 0x80, BleGattOperationType.CHARACTERISTIC_WRITE); - + .characteristicWriteCallback(characteristicUUID, (device, characteristic, bytes, result) -> { + if(writeData(characteristic, bytes)) { + result.success(); + } else { + result.failure(0x80); + // can also use result.disconnect(0x80); } - return Completable.complete(); }).build() ).build() ).build(); diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattReadResultMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattReadResultMock.java new file mode 100644 index 000000000..55286da84 --- /dev/null +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattReadResultMock.java @@ -0,0 +1,24 @@ +package com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results; + +/** + * An interface for the user to respond to a read request + */ +public interface RxBleGattReadResultMock { + /** + * Respond with success + * @param data The data to be returned in response to the read request + */ + void success(byte[] data); + + /** + * Respond with failure + * @param status The ATT status (error code) + */ + void failure(int status); + + /** + * Trigger a disconnection + * @param status The disconnection status (error code) + */ + void disconnect(int status); +} diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattWriteResultMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattWriteResultMock.java new file mode 100644 index 000000000..809ed6061 --- /dev/null +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattWriteResultMock.java @@ -0,0 +1,23 @@ +package com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results; + +/** + * An interface for the user to respond to a write request + */ +public interface RxBleGattWriteResultMock { + /** + * Respond with success + */ + void success(); + + /** + * Respond with failure + * @param status The ATT status (error code) + */ + void failure(int status); + + /** + * Trigger a disconnection + * @param status The disconnection status (error code) + */ + void disconnect(int status); +} diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicReadCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicReadCallback.java new file mode 100644 index 000000000..411a0e9a7 --- /dev/null +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicReadCallback.java @@ -0,0 +1,22 @@ +package com.polidea.rxandroidble2.mockrxandroidble.Callbacks; + +import android.bluetooth.BluetoothGattCharacteristic; + +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattReadResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.RxBleDeviceMock; + +/** + * An interface for a user callback for handling characteristic read requests + */ +public interface RxBleCharacteristicReadCallback extends RxBleReadCallback { + + /** + * Handles a read on a GATT characteristic + * @param device the device being read from + * @param characteristic the characteristic being read from + * @param result the result handler + * @throws Exception on error + */ + @Override + void handle(RxBleDeviceMock device, BluetoothGattCharacteristic characteristic, RxBleGattReadResultMock result) throws Exception; +} diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicWriteCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicWriteCallback.java new file mode 100644 index 000000000..026d671a5 --- /dev/null +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicWriteCallback.java @@ -0,0 +1,26 @@ +package com.polidea.rxandroidble2.mockrxandroidble.Callbacks; + +import android.bluetooth.BluetoothGattCharacteristic; + +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattWriteResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.RxBleDeviceMock; + +/** + * An interface for a user callback for handling characteristic write requests + */ +public interface RxBleCharacteristicWriteCallback extends RxBleWriteCallback { + + /** + * Handles a write on a GATT characteristic + * @param device the device being written to + * @param characteristic the characteristic being written to + * @param data the data being written + * @param result the result handler + * @throws Exception on error + */ + @Override + void handle(RxBleDeviceMock device, + BluetoothGattCharacteristic characteristic, + byte[] data, + RxBleGattWriteResultMock result) throws Exception; +} diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorReadCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorReadCallback.java new file mode 100644 index 000000000..55b7ca75e --- /dev/null +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorReadCallback.java @@ -0,0 +1,22 @@ +package com.polidea.rxandroidble2.mockrxandroidble.Callbacks; + +import android.bluetooth.BluetoothGattDescriptor; + +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattReadResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.RxBleDeviceMock; + +/** + * An interface for a user callback for handling descriptor read requests + */ +public interface RxBleDescriptorReadCallback extends RxBleReadCallback { + + /** + * Handles a read on a GATT descriptor + * @param device the device being read from + * @param descriptor the descriptor being read from + * @param result the result handler + * @throws Exception on error + */ + @Override + void handle(RxBleDeviceMock device, BluetoothGattDescriptor descriptor, RxBleGattReadResultMock result) throws Exception; +} diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorWriteCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorWriteCallback.java new file mode 100644 index 000000000..869d60653 --- /dev/null +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorWriteCallback.java @@ -0,0 +1,23 @@ +package com.polidea.rxandroidble2.mockrxandroidble.Callbacks; + +import android.bluetooth.BluetoothGattDescriptor; + +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattWriteResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.RxBleDeviceMock; + +/** + * An interface for a user callback for handling descriptor write requests + */ +public interface RxBleDescriptorWriteCallback extends RxBleWriteCallback { + + /** + * Handles a write on a GATT descriptor + * @param device the device being written to + * @param descriptor the descriptor being written to + * @param data the data being written + * @param result the result handler + * @throws Exception on error + */ + @Override + void handle(RxBleDeviceMock device, BluetoothGattDescriptor descriptor, byte[] data, RxBleGattWriteResultMock result) throws Exception; +} diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleReadCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleReadCallback.java new file mode 100644 index 000000000..34f2dc020 --- /dev/null +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleReadCallback.java @@ -0,0 +1,21 @@ +package com.polidea.rxandroidble2.mockrxandroidble.Callbacks; + +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattReadResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.RxBleDeviceMock; + +import io.reactivex.annotations.NonNull; + +/** + * A generic interface for a user callback for handling read requests + * @param The type of attribute ({@link android.bluetooth.BluetoothGattCharacteristic} or {@link android.bluetooth.BluetoothGattDescriptor}) + */ +public interface RxBleReadCallback { + /** + * Handles a read on a GATT attribute + * @param device the device being read from + * @param attribute the attribute being read from + * @param result the result handler + * @throws Exception on error + */ + void handle(@NonNull RxBleDeviceMock device, @NonNull T attribute, @NonNull RxBleGattReadResultMock result) throws Exception; +} diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleWriteCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleWriteCallback.java new file mode 100644 index 000000000..1e5bc0922 --- /dev/null +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleWriteCallback.java @@ -0,0 +1,26 @@ +package com.polidea.rxandroidble2.mockrxandroidble.Callbacks; + + +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattWriteResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.RxBleDeviceMock; + +import io.reactivex.annotations.NonNull; + +/** + * A generic interface for a user callback for handling write requests + * @param The type of attribute ({@link android.bluetooth.BluetoothGattCharacteristic} or {@link android.bluetooth.BluetoothGattDescriptor}) + */ +public interface RxBleWriteCallback { + /** + * Handles a write on a GATT attribute + * @param device the device being written to + * @param attribute the attribute being written to + * @param data the data being written + * @param result the result handler + * @throws Exception on error + */ + void handle(@NonNull RxBleDeviceMock device, + @NonNull T attribute, + @NonNull byte[] data, + @NonNull RxBleGattWriteResultMock result) throws Exception; +} diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java index 3e5d2323a..6ec43cde9 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java @@ -13,9 +13,19 @@ import com.polidea.rxandroidble2.RxBleCustomOperation; import com.polidea.rxandroidble2.RxBleDeviceServices; import com.polidea.rxandroidble2.exceptions.BleConflictingNotificationAlreadySetException; +import com.polidea.rxandroidble2.exceptions.BleDisconnectedException; +import com.polidea.rxandroidble2.exceptions.BleGattCharacteristicException; +import com.polidea.rxandroidble2.exceptions.BleGattDescriptorException; +import com.polidea.rxandroidble2.exceptions.BleGattOperationType; import com.polidea.rxandroidble2.internal.Priority; import com.polidea.rxandroidble2.internal.connection.ImmediateSerializedBatchAckStrategy; import com.polidea.rxandroidble2.internal.util.ObservableUtil; +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattReadResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattWriteResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleCharacteristicReadCallback; +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleCharacteristicWriteCallback; +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleDescriptorReadCallback; +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleDescriptorWriteCallback; import java.util.ArrayList; import java.util.HashMap; @@ -33,12 +43,12 @@ import io.reactivex.SingleSource; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Action; -import io.reactivex.functions.BiFunction; import io.reactivex.functions.Consumer; import io.reactivex.functions.Function; import io.reactivex.functions.Predicate; import io.reactivex.internal.functions.Functions; - +import io.reactivex.subjects.CompletableSubject; +import io.reactivex.subjects.SingleSubject; public class RxBleConnectionMock implements RxBleConnection { @@ -64,18 +74,19 @@ public class RxBleConnectionMock implements RxBleConnection { private int rssi; private int currentMtu = 23; private Map> characteristicNotificationSources; - private Map>> characteristicReadCallbacks; - private Map> characteristicWriteCallbacks; - private Map>>> descriptorReadCallbacks; - private Map>> descriptorWriteCallbacks; + private Map characteristicReadCallbacks; + private Map characteristicWriteCallbacks; + private Map> descriptorReadCallbacks; + private Map> descriptorWriteCallbacks; + private RxBleDeviceMock deviceMock; public RxBleConnectionMock(RxBleDeviceServices rxBleDeviceServices, int rssi, Map> characteristicNotificationSources, - Map>> characteristicReadCallbacks, - Map> characteristicWriteCallbacks, - Map>>> descriptorReadCallbacks, - Map>> descriptorWriteCallbacks) { + Map characteristicReadCallbacks, + Map characteristicWriteCallbacks, + Map> descriptorReadCallbacks, + Map> descriptorWriteCallbacks) { this.rxBleDeviceServices = rxBleDeviceServices; this.rssi = rssi; this.characteristicNotificationSources = characteristicNotificationSources; @@ -85,6 +96,10 @@ public RxBleConnectionMock(RxBleDeviceServices rxBleDeviceServices, this.descriptorWriteCallbacks = descriptorWriteCallbacks; } + void setDeviceMock(RxBleDeviceMock deviceMock) { + this.deviceMock = deviceMock; + } + @Override public Completable requestConnectionPriority(int connectionPriority, long delay, @@ -154,6 +169,12 @@ public SingleSource apply(RxBleDeviceServ }); } + private BleDisconnectedException handleDisconnection(int status) { + BleDisconnectedException e = new BleDisconnectedException(deviceMock.getMacAddress(), status); + deviceMock.disconnectWithException(e); + return e; + } + @Override public Single readCharacteristic(@NonNull UUID characteristicUuid) { return getCharacteristic(characteristicUuid).flatMap(new Function>() { @@ -166,11 +187,39 @@ public SingleSource apply(final BluetoothGattCharacteristic ch @Override public Single readCharacteristic(@NonNull final BluetoothGattCharacteristic characteristic) { - Function> readCallback = characteristicReadCallbacks.get(characteristic.getUuid()); + final RxBleCharacteristicReadCallback readCallback = characteristicReadCallbacks.get(characteristic.getUuid()); if (readCallback == null) { return Single.just(characteristic.getValue()); } - return Single.just(characteristic).flatMap(readCallback).doOnSuccess(new Consumer() { + return Single.just(characteristic).flatMap(new Function>() { + @Override + public SingleSource apply(final BluetoothGattCharacteristic characteristic) throws Exception { + final SingleSubject subj = SingleSubject.create(); + readCallback.handle(deviceMock, characteristic, new RxBleGattReadResultMock() { + @Override + public void success(byte[] data) { + characteristic.setValue(data); + subj.onSuccess(data); + } + + @Override + public void failure(int status) { + subj.onError( + new BleGattCharacteristicException(null, + characteristic, + status, + BleGattOperationType.CHARACTERISTIC_READ) + ); + } + + @Override + public void disconnect(int status) { + subj.onError(handleDisconnection(status)); + } + }); + return subj; + } + }).doOnSuccess(new Consumer() { @Override public void accept(byte[] bytes) throws Exception { characteristic.setValue(bytes); @@ -198,15 +247,38 @@ public SingleSource apply(final BluetoothGattDescriptor descri @Override public Single readDescriptor(@NonNull final BluetoothGattDescriptor descriptor) { - Map>> descriptorCallbacks = descriptorReadCallbacks + Map descriptorCallbacks = descriptorReadCallbacks .get(descriptor.getCharacteristic().getUuid()); if (descriptorCallbacks != null) { - Function> readCallback = descriptorCallbacks.get(descriptor.getUuid()); + final RxBleDescriptorReadCallback readCallback = descriptorCallbacks.get(descriptor.getUuid()); if (readCallback != null) { - return Single.just(descriptor).flatMap(readCallback).doOnSuccess(new Consumer() { + return Single.just(descriptor).flatMap(new Function>() { @Override - public void accept(byte[] bytes) throws Exception { - descriptor.setValue(bytes); + public SingleSource apply(final BluetoothGattDescriptor descriptor) throws Exception { + final SingleSubject subj = SingleSubject.create(); + readCallback.handle(deviceMock, descriptor, new RxBleGattReadResultMock() { + @Override + public void success(byte[] data) { + descriptor.setValue(data); + subj.onSuccess(data); + } + + @Override + public void failure(int status) { + subj.onError( + new BleGattDescriptorException(null, + descriptor, + status, + BleGattOperationType.DESCRIPTOR_READ) + ); + } + + @Override + public void disconnect(int status) { + subj.onError(handleDisconnection(status)); + } + }); + return subj; } }); } @@ -320,7 +392,7 @@ public Observable> setupIndication(@NonNull BluetoothGattChar @Override public Single writeCharacteristic(@NonNull BluetoothGattCharacteristic bluetoothGattCharacteristic, @NonNull final byte[] data) { - final BiFunction writeCallback = characteristicWriteCallbacks + final RxBleCharacteristicWriteCallback writeCallback = characteristicWriteCallbacks .get(bluetoothGattCharacteristic.getUuid()); if (writeCallback == null) { bluetoothGattCharacteristic.setValue(data); @@ -331,15 +403,30 @@ public Single writeCharacteristic(@NonNull BluetoothGattCharacteristic b .flatMap(new Function>() { @Override public SingleSource apply(final BluetoothGattCharacteristic characteristic) throws Exception { - return writeCallback - .apply(characteristic, data) - .toSingleDefault(data) - .doOnSuccess(new Consumer() { + final SingleSubject subj = SingleSubject.create(); + writeCallback.handle(deviceMock, characteristic, data, new RxBleGattWriteResultMock() { + @Override + public void success() { + characteristic.setValue(data); + subj.onSuccess(data); + } + + @Override + public void failure(int status) { + subj.onError( + new BleGattCharacteristicException(null, + characteristic, + status, + BleGattOperationType.CHARACTERISTIC_WRITE) + ); + } + @Override - public void accept(byte[] bytes) throws Exception { - characteristic.setValue(bytes); - } - }); + public void disconnect(int status) { + subj.onError(handleDisconnection(status)); + } + }); + return subj; } }); } @@ -467,21 +554,36 @@ public Completable apply(final BluetoothGattDescriptor descriptor) throws Except @Override public Completable writeDescriptor(@NonNull final BluetoothGattDescriptor descriptor, @NonNull final byte[] data) { - Map> descriptorCallbacks = descriptorWriteCallbacks + Map descriptorCallbacks = descriptorWriteCallbacks .get(descriptor.getCharacteristic().getUuid()); if (descriptorCallbacks != null) { - final BiFunction writeCallback = descriptorCallbacks.get(descriptor.getUuid()); + final RxBleDescriptorWriteCallback writeCallback = descriptorCallbacks.get(descriptor.getUuid()); if (writeCallback != null) { // wrap descriptor in single so exceptions from writeCallback are handled for us return Single.just(descriptor).flatMapCompletable(new Function() { @Override public Completable apply(final BluetoothGattDescriptor descriptor) throws Exception { - return writeCallback.apply(descriptor, data).doOnComplete(new Action() { + final CompletableSubject subj = CompletableSubject.create(); + writeCallback.handle(deviceMock, descriptor, data, new RxBleGattWriteResultMock() { @Override - public void run() throws Exception { + public void success() { descriptor.setValue(data); + subj.onComplete(); + } + + @Override + public void failure(int status) { + subj.onError( + new BleGattDescriptorException(null, descriptor, status, BleGattOperationType.DESCRIPTOR_WRITE) + ); + } + + @Override + public void disconnect(int status) { + subj.onError(handleDisconnection(status)); } }); + return subj; } }); } @@ -583,10 +685,10 @@ public static class Builder { private RxBleDeviceServices rxBleDeviceServices; private int rssi; private Map> characteristicNotificationSources = new HashMap<>(); - private Map>> characteristicReadCallbacks = new HashMap<>(); - private Map> characteristicWriteCallbacks = new HashMap<>(); - private Map>>> descriptorReadCallbacks = new HashMap<>(); - private Map>> descriptorWriteCallbacks = new HashMap<>(); + private Map characteristicReadCallbacks = new HashMap<>(); + private Map characteristicWriteCallbacks = new HashMap<>(); + private Map> descriptorReadCallbacks = new HashMap<>(); + private Map> descriptorWriteCallbacks = new HashMap<>(); public Builder() { @@ -649,8 +751,7 @@ public Builder notificationSource(@NonNull UUID characteristicUUID, @NonNull Obs * @param readCallback The callback */ public Builder characteristicReadCallback(@NonNull UUID characteristicUUID, - @NonNull Function> readCallback) { + @NonNull RxBleCharacteristicReadCallback readCallback) { characteristicReadCallbacks.put(characteristicUUID, readCallback); return this; } @@ -662,7 +763,7 @@ public Builder characteristicReadCallback(@NonNull UUID characteristicUUID, * @param writeCallback The callback */ public Builder characteristicWriteCallback(@NonNull UUID characteristicUUID, - @NonNull BiFunction writeCallback) { + @NonNull RxBleCharacteristicWriteCallback writeCallback) { characteristicWriteCallbacks.put(characteristicUUID, writeCallback); return this; } @@ -676,8 +777,8 @@ public Builder characteristicWriteCallback(@NonNull UUID characteristicUUID, */ public Builder descriptorReadCallback(@NonNull UUID characteristicUUID, @NonNull UUID descriptorUUID, - @NonNull Function> readCallback) { - Map>> descriptorCallbacks = descriptorReadCallbacks + @NonNull RxBleDescriptorReadCallback readCallback) { + Map descriptorCallbacks = descriptorReadCallbacks .get(characteristicUUID); if (descriptorCallbacks == null) { descriptorCallbacks = new HashMap<>(); @@ -696,8 +797,8 @@ public Builder descriptorReadCallback(@NonNull UUID characteristicUUID, */ public Builder descriptorWriteCallback(@NonNull UUID characteristicUUID, @NonNull UUID descriptorUUID, - @NonNull BiFunction writeCallback) { - Map> descriptorCallbacks = descriptorWriteCallbacks + @NonNull RxBleDescriptorWriteCallback writeCallback) { + Map descriptorCallbacks = descriptorWriteCallbacks .get(characteristicUUID); if (descriptorCallbacks == null) { descriptorCallbacks = new HashMap<>(); diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java index d8443431a..0d6eca4a0 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java @@ -2,7 +2,6 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import androidx.annotation.NonNull; @@ -13,9 +12,13 @@ import com.polidea.rxandroidble2.RxBleDeviceServices; import com.polidea.rxandroidble2.Timeout; import com.polidea.rxandroidble2.exceptions.BleAlreadyConnectedException; +import com.polidea.rxandroidble2.exceptions.BleException; +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleCharacteristicReadCallback; +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleCharacteristicWriteCallback; +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleDescriptorReadCallback; +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleDescriptorWriteCallback; import com.polidea.rxandroidble2.scan.ScanRecord; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -23,15 +26,12 @@ import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; -import io.reactivex.Completable; import io.reactivex.Observable; -import io.reactivex.Single; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Action; -import io.reactivex.functions.BiFunction; import io.reactivex.functions.Consumer; -import io.reactivex.functions.Function; import io.reactivex.subjects.BehaviorSubject; +import io.reactivex.subjects.ReplaySubject; import static com.polidea.rxandroidble2.RxBleConnection.RxBleConnectionState.CONNECTED; import static com.polidea.rxandroidble2.RxBleConnection.RxBleConnectionState.CONNECTING; @@ -39,6 +39,7 @@ public class RxBleDeviceMock implements RxBleDevice { + private ReplaySubject connectionSubject; private RxBleConnection rxBleConnection; private BehaviorSubject connectionStateBehaviorSubject = BehaviorSubject.createDefault( DISCONNECTED @@ -52,6 +53,19 @@ public class RxBleDeviceMock implements RxBleDevice { private BluetoothDevice bluetoothDevice; private AtomicBoolean isConnected = new AtomicBoolean(false); + private RxBleDeviceMock(String name, + String macAddress, + @Nullable BluetoothDevice bluetoothDevice, + RxBleConnectionMock connectionMock) { + this.name = name; + this.macAddress = macAddress; + this.rssi = connectionMock.getRssi(); + this.advertisedUUIDs = connectionMock.getServiceUuids(); + this.bluetoothDevice = bluetoothDevice; + this.rxBleConnection = connectionMock; + connectionMock.setDeviceMock(this); + } + public RxBleDeviceMock(String name, String macAddress, byte[] scanRecord, @@ -59,19 +73,20 @@ public RxBleDeviceMock(String name, RxBleDeviceServices rxBleDeviceServices, Map> characteristicNotificationSources, @Nullable BluetoothDevice bluetoothDevice) { - this.name = name; - this.macAddress = macAddress; - this.rxBleConnection = new RxBleConnectionMock(rxBleDeviceServices, - rssi, - characteristicNotificationSources, - new HashMap>>(), - new HashMap>(), - new HashMap>>>(), - new HashMap>>()); - this.rssi = rssi; + this( + name, + macAddress, + bluetoothDevice, + new RxBleConnectionMock( + rxBleDeviceServices, + rssi, + characteristicNotificationSources, + new HashMap(), + new HashMap(), + new HashMap>(), + new HashMap>()) + ); this.legacyScanRecord = scanRecord; - this.advertisedUUIDs = new ArrayList<>(); - this.bluetoothDevice = bluetoothDevice; } public RxBleDeviceMock(String name, @@ -80,13 +95,13 @@ public RxBleDeviceMock(String name, @Nullable BluetoothDevice bluetoothDevice, RxBleConnectionMock connectionMock ) { - this.name = name; - this.macAddress = macAddress; - this.rxBleConnection = connectionMock; - this.rssi = connectionMock.getRssi(); + this( + name, + macAddress, + bluetoothDevice, + connectionMock + ); this.scanRecord = scanRecord; - this.advertisedUUIDs = connectionMock.getServiceUuids(); - this.bluetoothDevice = bluetoothDevice; } public static class Builder { @@ -274,7 +289,20 @@ public Observable establishConnection(boolean autoConnect, Time } private Observable emitConnectionWithoutCompleting() { - return Observable.never().startWith(rxBleConnection); + connectionSubject = ReplaySubject.createWithSize(1); + connectionSubject.onNext(rxBleConnection); + return connectionSubject.doFinally(new Action() { + @Override + public void run() throws Exception { + connectionSubject = null; + } + }); + } + + public void disconnectWithException(BleException exception) { + if (connectionSubject != null) { + connectionSubject.onError(exception); + } } public List getAdvertisedUUIDs() { diff --git a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy index ce560c98c..0c9e46cb5 100644 --- a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy +++ b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy @@ -4,16 +4,19 @@ import android.os.Build import android.os.ParcelUuid import com.polidea.rxandroidble2.RxBleClient import com.polidea.rxandroidble2.RxBleConnection +import com.polidea.rxandroidble2.exceptions.BleDisconnectedException import com.polidea.rxandroidble2.exceptions.BleGattCharacteristicException import com.polidea.rxandroidble2.exceptions.BleGattDescriptorException import com.polidea.rxandroidble2.exceptions.BleGattOperationType +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattReadResultMock +import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattWriteResultMock import com.polidea.rxandroidble2.scan.ScanFilter; import com.polidea.rxandroidble2.scan.ScanSettings; import hkhc.electricspock.ElectricSpecification -import io.reactivex.Completable import io.reactivex.Observable import io.reactivex.Single import io.reactivex.subjects.PublishSubject +import org.junit.runner.notification.Failure import org.robolectric.annotation.Config @Config(manifest = Config.NONE, constants = BuildConfig, sdk = Build.VERSION_CODES.LOLLIPOP) @@ -30,28 +33,10 @@ public class RxBleClientMockTest extends ElectricSpecification { RxBleClient rxBleClient PublishSubject characteristicNotificationSubject = PublishSubject.create() - PublishSubject characteristicReadSubject = PublishSubject.create() - PublishSubject characteristicWriteSubject = PublishSubject.create() - PublishSubject descriptorReadSubject = PublishSubject.create() - PublishSubject descriptorWriteSubject = PublishSubject.create() - - Single processReadSubject(PublishSubject subject) { - subject.take(1).singleOrError() - } - - Completable processWriteSubject(PublishSubject subject, BleGattOperationType type) { - subject.take(1).flatMapCompletable({ shouldSucceed -> - if(!shouldSucceed) { - if(type == BleGattOperationType.CHARACTERISTIC_WRITE) { - throw new BleGattCharacteristicException(null, null, 0x80, type); - } else { - throw new BleGattDescriptorException(null, null, 0x80, type); - } - } - Completable.complete(); - }) - } - + PublishSubject characteristicReadSubject = PublishSubject.create() + PublishSubject characteristicWriteSubject = PublishSubject.create() + PublishSubject descriptorReadSubject = PublishSubject.create() + PublishSubject descriptorWriteSubject = PublishSubject.create() def createDevice(deviceName, macAddress, rssi) { new RxBleDeviceMock.Builder() @@ -87,17 +72,17 @@ public class RxBleClientMockTest extends ElectricSpecification { .build() ).build() ) - .characteristicReadCallback(characteristicUUID, {characteristic -> - processReadSubject(characteristicReadSubject) + .characteristicReadCallback(characteristicUUID, { device, characteristic, result -> + characteristicReadSubject.onNext(result) }) - .characteristicWriteCallback(characteristicUUID, { characteristic, bytes -> - processWriteSubject(characteristicWriteSubject, BleGattOperationType.CHARACTERISTIC_WRITE) + .characteristicWriteCallback(characteristicUUID, { device, characteristic, bytes, result -> + characteristicWriteSubject.onNext(result) }) - .descriptorReadCallback(characteristicUUID, descriptorUUID, { descriptor -> - processReadSubject(descriptorReadSubject) + .descriptorReadCallback(characteristicUUID, descriptorUUID, { device, descriptor, result -> + descriptorReadSubject.onNext(result) }) - .descriptorWriteCallback(characteristicUUID, descriptorUUID, { descriptor, bytes -> - processWriteSubject(descriptorWriteSubject, BleGattOperationType.DESCRIPTOR_WRITE) + .descriptorWriteCallback(characteristicUUID, descriptorUUID, { device, descriptor, bytes, result -> + descriptorWriteSubject.onNext(result) }) .build() ).build() @@ -413,7 +398,8 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should return characteristic data via callback"() { - given: + when: + characteristicReadSubject.take(1).subscribe { result -> result.success("PolideaCB".getBytes()) } def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -422,15 +408,13 @@ public class RxBleClientMockTest extends ElectricSpecification { .map { data -> new String(data) } .test() - when: - characteristicReadSubject.onNext("PolideaCB".getBytes()) - then: testSubscriber.assertValue("PolideaCB") } def "should throw characteristic read error via callback"() { - given: + when: + characteristicReadSubject.take(1).subscribe { result -> result.failure(0x80) } def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -439,15 +423,32 @@ public class RxBleClientMockTest extends ElectricSpecification { .map { data -> new String(data) } .test() + then: + testSubscriber.assertError { throwable -> + return BleGattCharacteristicException.class.isInstance(throwable) && ((BleGattCharacteristicException) throwable).getStatus() == 0x80 + } + } + + def "should throw characteristic read disconnection via callback"() { when: - characteristicReadSubject.onError(new Throwable("read error")) + characteristicReadSubject.take(1).subscribe { result -> result.disconnect(0x80) } + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapSingle { rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID) } + .map { data -> new String(data) } + .test() then: - testSubscriber.assertErrorMessage("read error") + testSubscriber.assertError { throwable -> + return BleDisconnectedException.class.isInstance(throwable) && ((BleDisconnectedException) throwable).state == 0x80 + } } def "should write characteristic data via callback"() { - given: + when: + characteristicWriteSubject.take(1).subscribe { result -> result.success() } def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -456,15 +457,13 @@ public class RxBleClientMockTest extends ElectricSpecification { .map { data -> new String(data) } .test() - when: - characteristicWriteSubject.onNext(Boolean.TRUE); - then: testSubscriber.assertValue("PolideaCB") } def "should fail to write characteristic data via callback"() { - given: + when: + characteristicWriteSubject.take(1).subscribe { result -> result.failure(0x80) } def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -473,16 +472,26 @@ public class RxBleClientMockTest extends ElectricSpecification { .map { data -> new String(data) } .test() + then: + testSubscriber.assertError({ throwable -> + return BleGattCharacteristicException.class.isInstance(throwable) && ((BleGattCharacteristicException) throwable).getStatus() == 0x80 + }) + } + + def "should fail to write characteristic data due to disconnection via callback"() { when: - characteristicWriteSubject.onNext(Boolean.FALSE); + characteristicWriteSubject.take(1).subscribe { result -> result.disconnect(0x80) } + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapSingle { rxBleConnection -> rxBleConnection.writeCharacteristic(characteristicUUID, "PolideaCB".getBytes()) } + .map { data -> new String(data) } + .test() then: testSubscriber.assertError({ throwable -> - if (!BleGattCharacteristicException.class.isInstance(throwable)) { - return false; - } - BleGattCharacteristicException e = (BleGattCharacteristicException) throwable; - return e.getStatus() == 0x80 + return BleDisconnectedException.class.isInstance(throwable) && ((BleDisconnectedException) throwable).state == 0x80 }) } @@ -501,7 +510,8 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should return descriptor data via callback"() { - given: + when: + descriptorReadSubject.take(1).subscribe({ result -> result.success("ConfigCB".getBytes()) }) def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -510,15 +520,13 @@ public class RxBleClientMockTest extends ElectricSpecification { .map { data -> new String(data) } .test() - when: - descriptorReadSubject.onNext("ConfigCB".getBytes()) - then: testSubscriber.assertValue("ConfigCB") } def "should throw descriptor read error via callback"() { - given: + when: + descriptorReadSubject.take(1).subscribe({ result -> result.failure(0x80) }) def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -527,15 +535,32 @@ public class RxBleClientMockTest extends ElectricSpecification { .map { data -> new String(data) } .test() + then: + testSubscriber.assertError({ throwable -> + return BleGattDescriptorException.class.isInstance(throwable) && ((BleGattDescriptorException) throwable).getStatus() == 0x80 + }) + } + + def "should throw descriptor read disconnection error via callback"() { when: - descriptorReadSubject.onError(new Throwable("read error")) + descriptorReadSubject.take(1).subscribe({ result -> result.disconnect(0x80) }) + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) + .take(1) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .flatMapSingle { rxBleConnection -> rxBleConnection.readDescriptor(serviceUUID, characteristicUUID, descriptorUUID) } + .map { data -> new String(data) } + .test() then: - testSubscriber.assertErrorMessage("read error") + testSubscriber.assertError({ throwable -> + return BleDisconnectedException.class.isInstance(throwable) && ((BleDisconnectedException) throwable).state == 0x80 + }) } def "should write descriptor data via callback"() { - given: + when: + descriptorWriteSubject.take(1).subscribe({ result -> result.success() }) def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } @@ -543,15 +568,13 @@ public class RxBleClientMockTest extends ElectricSpecification { .flatMapCompletable { rxBleConnection -> rxBleConnection.writeDescriptor(serviceUUID, characteristicUUID, descriptorUUID, "ConfigCB".getBytes()) } .test() - when: - descriptorWriteSubject.onNext(Boolean.TRUE); - then: testSubscriber.assertComplete(); } def "should fail to write descriptor data via callback"() { - given: + when: + descriptorWriteSubject.take(1).subscribe({ result -> result.failure(0x80) }) def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } @@ -559,16 +582,25 @@ public class RxBleClientMockTest extends ElectricSpecification { .flatMapCompletable { rxBleConnection -> rxBleConnection.writeDescriptor(serviceUUID, characteristicUUID, descriptorUUID, "ConfigCB".getBytes()) } .test() + then: + testSubscriber.assertError({ throwable -> + return BleGattDescriptorException.class.isInstance(throwable) && ((BleGattDescriptorException) throwable).getStatus() == 0x80 + }) + } + + def "should fail to write descriptor data via callback due to disconnection"() { when: - descriptorWriteSubject.onNext(Boolean.FALSE); + descriptorWriteSubject.take(1).subscribe({ result -> result.disconnect(0x80) }) + def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) + .map { scanResult -> scanResult.getBleDevice() } + .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } + .take(1) + .flatMapCompletable { rxBleConnection -> rxBleConnection.writeDescriptor(serviceUUID, characteristicUUID, descriptorUUID, "ConfigCB".getBytes()) } + .test() then: testSubscriber.assertError({ throwable -> - if (!BleGattDescriptorException.class.isInstance(throwable)) { - return false; - } - BleGattDescriptorException e = (BleGattDescriptorException) throwable; - return e.getStatus() == 0x80 + return BleDisconnectedException.class.isInstance(throwable) && ((BleDisconnectedException) throwable).state == 0x80 }) } From 93521221baf077d37b89163e39f39255391ac347 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Tue, 11 Aug 2020 16:16:41 +0100 Subject: [PATCH 16/21] Fixed checkstyle violations --- .../mockrxandroidble/Callbacks/RxBleReadCallback.java | 3 ++- .../mockrxandroidble/Callbacks/RxBleWriteCallback.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleReadCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleReadCallback.java index 34f2dc020..be99669b0 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleReadCallback.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleReadCallback.java @@ -7,7 +7,8 @@ /** * A generic interface for a user callback for handling read requests - * @param The type of attribute ({@link android.bluetooth.BluetoothGattCharacteristic} or {@link android.bluetooth.BluetoothGattDescriptor}) + * @param The type of attribute ({@link android.bluetooth.BluetoothGattCharacteristic} or + * {@link android.bluetooth.BluetoothGattDescriptor}) */ public interface RxBleReadCallback { /** diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleWriteCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleWriteCallback.java index 1e5bc0922..8d45ab934 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleWriteCallback.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleWriteCallback.java @@ -8,7 +8,8 @@ /** * A generic interface for a user callback for handling write requests - * @param The type of attribute ({@link android.bluetooth.BluetoothGattCharacteristic} or {@link android.bluetooth.BluetoothGattDescriptor}) + * @param The type of attribute ({@link android.bluetooth.BluetoothGattCharacteristic} or + * {@link android.bluetooth.BluetoothGattDescriptor}) */ public interface RxBleWriteCallback { /** From 5ac59a0491ab85f75ed9710c733eeb36fb9b2ac7 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Tue, 11 Aug 2020 16:30:23 +0100 Subject: [PATCH 17/21] Fixed callback interfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I wanted to override the method in the base interface to rename a parameter. Looks like this can’t be done, so I just removed the base interfaces. --- .../RxBleCharacteristicReadCallback.java | 3 +-- .../RxBleCharacteristicWriteCallback.java | 3 +-- .../RxBleDescriptorReadCallback.java | 3 +-- .../RxBleDescriptorWriteCallback.java | 3 +-- .../Callbacks/RxBleReadCallback.java | 22 --------------- .../Callbacks/RxBleWriteCallback.java | 27 ------------------- 6 files changed, 4 insertions(+), 57 deletions(-) delete mode 100644 mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleReadCallback.java delete mode 100644 mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleWriteCallback.java diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicReadCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicReadCallback.java index 411a0e9a7..475066dcd 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicReadCallback.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicReadCallback.java @@ -8,7 +8,7 @@ /** * An interface for a user callback for handling characteristic read requests */ -public interface RxBleCharacteristicReadCallback extends RxBleReadCallback { +public interface RxBleCharacteristicReadCallback { /** * Handles a read on a GATT characteristic @@ -17,6 +17,5 @@ public interface RxBleCharacteristicReadCallback extends RxBleReadCallback { +public interface RxBleCharacteristicWriteCallback { /** * Handles a write on a GATT characteristic @@ -18,7 +18,6 @@ public interface RxBleCharacteristicWriteCallback extends RxBleWriteCallback { +public interface RxBleDescriptorReadCallback { /** * Handles a read on a GATT descriptor @@ -17,6 +17,5 @@ public interface RxBleDescriptorReadCallback extends RxBleReadCallback { +public interface RxBleDescriptorWriteCallback { /** * Handles a write on a GATT descriptor @@ -18,6 +18,5 @@ public interface RxBleDescriptorWriteCallback extends RxBleWriteCallback The type of attribute ({@link android.bluetooth.BluetoothGattCharacteristic} or - * {@link android.bluetooth.BluetoothGattDescriptor}) - */ -public interface RxBleReadCallback { - /** - * Handles a read on a GATT attribute - * @param device the device being read from - * @param attribute the attribute being read from - * @param result the result handler - * @throws Exception on error - */ - void handle(@NonNull RxBleDeviceMock device, @NonNull T attribute, @NonNull RxBleGattReadResultMock result) throws Exception; -} diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleWriteCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleWriteCallback.java deleted file mode 100644 index 8d45ab934..000000000 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleWriteCallback.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.polidea.rxandroidble2.mockrxandroidble.Callbacks; - - -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattWriteResultMock; -import com.polidea.rxandroidble2.mockrxandroidble.RxBleDeviceMock; - -import io.reactivex.annotations.NonNull; - -/** - * A generic interface for a user callback for handling write requests - * @param The type of attribute ({@link android.bluetooth.BluetoothGattCharacteristic} or - * {@link android.bluetooth.BluetoothGattDescriptor}) - */ -public interface RxBleWriteCallback { - /** - * Handles a write on a GATT attribute - * @param device the device being written to - * @param attribute the attribute being written to - * @param data the data being written - * @param result the result handler - * @throws Exception on error - */ - void handle(@NonNull RxBleDeviceMock device, - @NonNull T attribute, - @NonNull byte[] data, - @NonNull RxBleGattWriteResultMock result) throws Exception; -} From 602e356d4c3414bf841473e82230d8eb3512bc91 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Thu, 13 Aug 2020 11:52:22 +0100 Subject: [PATCH 18/21] Make new namespaces lower case --- .../mockrxandroidble/RxBleConnectionMock.java | 12 ++++++------ .../mockrxandroidble/RxBleDeviceMock.java | 8 ++++---- .../RxBleCharacteristicReadCallback.java | 4 ++-- .../RxBleCharacteristicWriteCallback.java | 4 ++-- .../RxBleDescriptorReadCallback.java | 4 ++-- .../RxBleDescriptorWriteCallback.java | 4 ++-- .../results}/RxBleGattReadResultMock.java | 2 +- .../results}/RxBleGattWriteResultMock.java | 2 +- .../mockrxandroidble/RxBleClientMockTest.groovy | 4 ++-- 9 files changed, 22 insertions(+), 22 deletions(-) rename mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/{Callbacks => callbacks}/RxBleCharacteristicReadCallback.java (84%) rename mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/{Callbacks => callbacks}/RxBleCharacteristicWriteCallback.java (86%) rename mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/{Callbacks => callbacks}/RxBleDescriptorReadCallback.java (83%) rename mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/{Callbacks => callbacks}/RxBleDescriptorWriteCallback.java (84%) rename mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/{Callbacks/Results => callbacks/results}/RxBleGattReadResultMock.java (89%) rename mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/{Callbacks/Results => callbacks/results}/RxBleGattWriteResultMock.java (87%) diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java index 6ec43cde9..61065ba65 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMock.java @@ -20,12 +20,12 @@ import com.polidea.rxandroidble2.internal.Priority; import com.polidea.rxandroidble2.internal.connection.ImmediateSerializedBatchAckStrategy; import com.polidea.rxandroidble2.internal.util.ObservableUtil; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattReadResultMock; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattWriteResultMock; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleCharacteristicReadCallback; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleCharacteristicWriteCallback; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleDescriptorReadCallback; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleDescriptorWriteCallback; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattReadResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattWriteResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.RxBleCharacteristicReadCallback; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.RxBleCharacteristicWriteCallback; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.RxBleDescriptorReadCallback; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.RxBleDescriptorWriteCallback; import java.util.ArrayList; import java.util.HashMap; diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java index 0d6eca4a0..d4e208ef2 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/RxBleDeviceMock.java @@ -13,10 +13,10 @@ import com.polidea.rxandroidble2.Timeout; import com.polidea.rxandroidble2.exceptions.BleAlreadyConnectedException; import com.polidea.rxandroidble2.exceptions.BleException; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleCharacteristicReadCallback; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleCharacteristicWriteCallback; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleDescriptorReadCallback; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.RxBleDescriptorWriteCallback; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.RxBleCharacteristicReadCallback; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.RxBleCharacteristicWriteCallback; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.RxBleDescriptorReadCallback; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.RxBleDescriptorWriteCallback; import com.polidea.rxandroidble2.scan.ScanRecord; import java.util.HashMap; diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicReadCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/RxBleCharacteristicReadCallback.java similarity index 84% rename from mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicReadCallback.java rename to mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/RxBleCharacteristicReadCallback.java index 475066dcd..4aeaa5b22 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicReadCallback.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/RxBleCharacteristicReadCallback.java @@ -1,8 +1,8 @@ -package com.polidea.rxandroidble2.mockrxandroidble.Callbacks; +package com.polidea.rxandroidble2.mockrxandroidble.callbacks; import android.bluetooth.BluetoothGattCharacteristic; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattReadResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattReadResultMock; import com.polidea.rxandroidble2.mockrxandroidble.RxBleDeviceMock; /** diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicWriteCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/RxBleCharacteristicWriteCallback.java similarity index 86% rename from mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicWriteCallback.java rename to mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/RxBleCharacteristicWriteCallback.java index 1a06a5e78..a0b45ff2b 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleCharacteristicWriteCallback.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/RxBleCharacteristicWriteCallback.java @@ -1,8 +1,8 @@ -package com.polidea.rxandroidble2.mockrxandroidble.Callbacks; +package com.polidea.rxandroidble2.mockrxandroidble.callbacks; import android.bluetooth.BluetoothGattCharacteristic; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattWriteResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattWriteResultMock; import com.polidea.rxandroidble2.mockrxandroidble.RxBleDeviceMock; /** diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorReadCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/RxBleDescriptorReadCallback.java similarity index 83% rename from mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorReadCallback.java rename to mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/RxBleDescriptorReadCallback.java index 2671dc3c1..66700a3d6 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorReadCallback.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/RxBleDescriptorReadCallback.java @@ -1,8 +1,8 @@ -package com.polidea.rxandroidble2.mockrxandroidble.Callbacks; +package com.polidea.rxandroidble2.mockrxandroidble.callbacks; import android.bluetooth.BluetoothGattDescriptor; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattReadResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattReadResultMock; import com.polidea.rxandroidble2.mockrxandroidble.RxBleDeviceMock; /** diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorWriteCallback.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/RxBleDescriptorWriteCallback.java similarity index 84% rename from mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorWriteCallback.java rename to mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/RxBleDescriptorWriteCallback.java index 7b8291676..48cfae473 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/RxBleDescriptorWriteCallback.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/RxBleDescriptorWriteCallback.java @@ -1,8 +1,8 @@ -package com.polidea.rxandroidble2.mockrxandroidble.Callbacks; +package com.polidea.rxandroidble2.mockrxandroidble.callbacks; import android.bluetooth.BluetoothGattDescriptor; -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattWriteResultMock; +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattWriteResultMock; import com.polidea.rxandroidble2.mockrxandroidble.RxBleDeviceMock; /** diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattReadResultMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/results/RxBleGattReadResultMock.java similarity index 89% rename from mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattReadResultMock.java rename to mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/results/RxBleGattReadResultMock.java index 55286da84..a99cd9bb2 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattReadResultMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/results/RxBleGattReadResultMock.java @@ -1,4 +1,4 @@ -package com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results; +package com.polidea.rxandroidble2.mockrxandroidble.callbacks.results; /** * An interface for the user to respond to a read request diff --git a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattWriteResultMock.java b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/results/RxBleGattWriteResultMock.java similarity index 87% rename from mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattWriteResultMock.java rename to mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/results/RxBleGattWriteResultMock.java index 809ed6061..b7642e1ca 100644 --- a/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/Callbacks/Results/RxBleGattWriteResultMock.java +++ b/mockrxandroidble/src/main/java/com/polidea/rxandroidble2/mockrxandroidble/callbacks/results/RxBleGattWriteResultMock.java @@ -1,4 +1,4 @@ -package com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results; +package com.polidea.rxandroidble2.mockrxandroidble.callbacks.results; /** * An interface for the user to respond to a write request diff --git a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy index 0c9e46cb5..d8f84392f 100644 --- a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy +++ b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy @@ -8,8 +8,8 @@ import com.polidea.rxandroidble2.exceptions.BleDisconnectedException import com.polidea.rxandroidble2.exceptions.BleGattCharacteristicException import com.polidea.rxandroidble2.exceptions.BleGattDescriptorException import com.polidea.rxandroidble2.exceptions.BleGattOperationType -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattReadResultMock -import com.polidea.rxandroidble2.mockrxandroidble.Callbacks.Results.RxBleGattWriteResultMock +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattReadResultMock +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattWriteResultMock import com.polidea.rxandroidble2.scan.ScanFilter; import com.polidea.rxandroidble2.scan.ScanSettings; import hkhc.electricspock.ElectricSpecification From c4901e78835a5d7f677e157769581b92a8783f1e Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Thu, 13 Aug 2020 11:54:37 +0100 Subject: [PATCH 19/21] Remove unused imports from unit test --- .../rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy | 3 --- 1 file changed, 3 deletions(-) diff --git a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy index d8f84392f..6a88feab0 100644 --- a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy +++ b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy @@ -7,16 +7,13 @@ import com.polidea.rxandroidble2.RxBleConnection import com.polidea.rxandroidble2.exceptions.BleDisconnectedException import com.polidea.rxandroidble2.exceptions.BleGattCharacteristicException import com.polidea.rxandroidble2.exceptions.BleGattDescriptorException -import com.polidea.rxandroidble2.exceptions.BleGattOperationType import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattReadResultMock import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattWriteResultMock import com.polidea.rxandroidble2.scan.ScanFilter; import com.polidea.rxandroidble2.scan.ScanSettings; import hkhc.electricspock.ElectricSpecification import io.reactivex.Observable -import io.reactivex.Single import io.reactivex.subjects.PublishSubject -import org.junit.runner.notification.Failure import org.robolectric.annotation.Config @Config(manifest = Config.NONE, constants = BuildConfig, sdk = Build.VERSION_CODES.LOLLIPOP) From 096d93de990a4445a15c5b3ddf5ab326a58f9502 Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Thu, 13 Aug 2020 12:13:24 +0100 Subject: [PATCH 20/21] Added convenience functions for read result closures to tests --- .../RxBleClientMockTest.groovy | 64 +++++++++++++++---- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy index 6a88feab0..e89a903d7 100644 --- a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy +++ b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy @@ -35,6 +35,22 @@ public class RxBleClientMockTest extends ElectricSpecification { PublishSubject descriptorReadSubject = PublishSubject.create() PublishSubject descriptorWriteSubject = PublishSubject.create() + def nextCharacteristicReadWillResult(closure) { + characteristicReadSubject.take(1).subscribe(closure) + } + + def nextCharacteristicWriteWillResult(closure) { + characteristicWriteSubject.take(1).subscribe(closure) + } + + def nextDescriptorReadWillResult(closure) { + descriptorReadSubject.take(1).subscribe(closure) + } + + def nextDescriptorWriteWillResult(closure) { + descriptorWriteSubject.take(1).subscribe(closure) + } + def createDevice(deviceName, macAddress, rssi) { new RxBleDeviceMock.Builder() .deviceMacAddress(macAddress) @@ -395,8 +411,10 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should return characteristic data via callback"() { + given: + nextCharacteristicReadWillResult { result -> result.success("PolideaCB".getBytes()) } + when: - characteristicReadSubject.take(1).subscribe { result -> result.success("PolideaCB".getBytes()) } def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -410,8 +428,10 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should throw characteristic read error via callback"() { + given: + nextCharacteristicReadWillResult { result -> result.failure(0x80) } + when: - characteristicReadSubject.take(1).subscribe { result -> result.failure(0x80) } def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -427,8 +447,10 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should throw characteristic read disconnection via callback"() { + given: + nextCharacteristicReadWillResult { result -> result.disconnect(0x80) } + when: - characteristicReadSubject.take(1).subscribe { result -> result.disconnect(0x80) } def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -444,8 +466,10 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should write characteristic data via callback"() { + given: + nextCharacteristicWriteWillResult { result -> result.success() } + when: - characteristicWriteSubject.take(1).subscribe { result -> result.success() } def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -459,8 +483,10 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should fail to write characteristic data via callback"() { + given: + nextCharacteristicWriteWillResult { result -> result.failure(0x80) } + when: - characteristicWriteSubject.take(1).subscribe { result -> result.failure(0x80) } def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -476,8 +502,10 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should fail to write characteristic data due to disconnection via callback"() { + given: + nextCharacteristicWriteWillResult { result -> result.disconnect(0x80) } + when: - characteristicWriteSubject.take(1).subscribe { result -> result.disconnect(0x80) } def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -507,8 +535,10 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should return descriptor data via callback"() { + given: + nextDescriptorReadWillResult { result -> result.success("ConfigCB".getBytes()) } + when: - descriptorReadSubject.take(1).subscribe({ result -> result.success("ConfigCB".getBytes()) }) def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -522,8 +552,10 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should throw descriptor read error via callback"() { + given: + nextDescriptorReadWillResult { result -> result.failure(0x80) } + when: - descriptorReadSubject.take(1).subscribe({ result -> result.failure(0x80) }) def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -539,8 +571,10 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should throw descriptor read disconnection error via callback"() { + given: + nextDescriptorReadWillResult { result -> result.disconnect(0x80) } + when: - descriptorReadSubject.take(1).subscribe({ result -> result.disconnect(0x80) }) def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .take(1) .map { scanResult -> scanResult.getBleDevice() } @@ -556,8 +590,10 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should write descriptor data via callback"() { + given: + nextDescriptorWriteWillResult { result -> result.success() } + when: - descriptorWriteSubject.take(1).subscribe({ result -> result.success() }) def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } @@ -570,8 +606,10 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should fail to write descriptor data via callback"() { + given: + nextDescriptorWriteWillResult { result -> result.failure(0x80) } + when: - descriptorWriteSubject.take(1).subscribe({ result -> result.failure(0x80) }) def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } @@ -586,8 +624,10 @@ public class RxBleClientMockTest extends ElectricSpecification { } def "should fail to write descriptor data via callback due to disconnection"() { + given: + nextDescriptorWriteWillResult { result -> result.disconnect(0x80) } + when: - descriptorWriteSubject.take(1).subscribe({ result -> result.disconnect(0x80) }) def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) .map { scanResult -> scanResult.getBleDevice() } .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } From 858838f860b161ee2a3990f91544f51941a3918e Mon Sep 17 00:00:00 2001 From: Nick Brook Date: Thu, 13 Aug 2020 12:50:23 +0100 Subject: [PATCH 21/21] Move connection mock tests to seperate test suite --- .../RxBleClientMockTest.groovy | 335 ----------------- .../RxBleConnectionMockTest.groovy | 349 ++++++++++++++++++ 2 files changed, 349 insertions(+), 335 deletions(-) create mode 100644 mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMockTest.groovy diff --git a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy index e89a903d7..66f874bbf 100644 --- a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy +++ b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleClientMockTest.groovy @@ -4,15 +4,9 @@ import android.os.Build import android.os.ParcelUuid import com.polidea.rxandroidble2.RxBleClient import com.polidea.rxandroidble2.RxBleConnection -import com.polidea.rxandroidble2.exceptions.BleDisconnectedException -import com.polidea.rxandroidble2.exceptions.BleGattCharacteristicException -import com.polidea.rxandroidble2.exceptions.BleGattDescriptorException -import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattReadResultMock -import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattWriteResultMock import com.polidea.rxandroidble2.scan.ScanFilter; import com.polidea.rxandroidble2.scan.ScanSettings; import hkhc.electricspock.ElectricSpecification -import io.reactivex.Observable import io.reactivex.subjects.PublishSubject import org.robolectric.annotation.Config @@ -23,33 +17,10 @@ public class RxBleClientMockTest extends ElectricSpecification { def serviceUUID2 = UUID.fromString("00001235-0000-0000-8000-000000000000") def characteristicUUID = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fb") def characteristicUUIDNoCallback = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fc") - def characteristicNotifiedUUID = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fb") def characteristicData = "Polidea".getBytes() def descriptorUUID = UUID.fromString("00001337-0000-1000-8000-00805f9b34fb") def descriptorData = "Config".getBytes() RxBleClient rxBleClient - PublishSubject characteristicNotificationSubject = PublishSubject.create() - - PublishSubject characteristicReadSubject = PublishSubject.create() - PublishSubject characteristicWriteSubject = PublishSubject.create() - PublishSubject descriptorReadSubject = PublishSubject.create() - PublishSubject descriptorWriteSubject = PublishSubject.create() - - def nextCharacteristicReadWillResult(closure) { - characteristicReadSubject.take(1).subscribe(closure) - } - - def nextCharacteristicWriteWillResult(closure) { - characteristicWriteSubject.take(1).subscribe(closure) - } - - def nextDescriptorReadWillResult(closure) { - descriptorReadSubject.take(1).subscribe(closure) - } - - def nextDescriptorWriteWillResult(closure) { - descriptorWriteSubject.take(1).subscribe(closure) - } def createDevice(deviceName, macAddress, rssi) { new RxBleDeviceMock.Builder() @@ -68,7 +39,6 @@ public class RxBleClientMockTest extends ElectricSpecification { ) .connection(new RxBleConnectionMock.Builder() .rssi(rssi) - .notificationSource(characteristicNotifiedUUID, characteristicNotificationSubject) .addService(serviceUUID, new RxBleClientMock.CharacteristicsBuilder() .addCharacteristic( @@ -85,18 +55,6 @@ public class RxBleClientMockTest extends ElectricSpecification { .build() ).build() ) - .characteristicReadCallback(characteristicUUID, { device, characteristic, result -> - characteristicReadSubject.onNext(result) - }) - .characteristicWriteCallback(characteristicUUID, { device, characteristic, bytes, result -> - characteristicWriteSubject.onNext(result) - }) - .descriptorReadCallback(characteristicUUID, descriptorUUID, { device, descriptor, result -> - descriptorReadSubject.onNext(result) - }) - .descriptorWriteCallback(characteristicUUID, descriptorUUID, { device, descriptor, bytes, result -> - descriptorWriteSubject.onNext(result) - }) .build() ).build() } @@ -345,19 +303,6 @@ public class RxBleClientMockTest extends ElectricSpecification { testSubscriber.assertValue(42) } - def "should return the BluetoothDevice mtu"() { - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.requestMtu(72) } - .test() - - then: - testSubscriber.assertValue(72) - } - def "should return BluetoothDevices that were added on the fly"() { given: def discoverableDevicesSubject = PublishSubject.create() @@ -379,286 +324,6 @@ public class RxBleClientMockTest extends ElectricSpecification { testRssiSubscriber.assertValues(42, 17) } - def "should return services list"() { - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> - rxBleConnection.discoverServices() - .map { rxBleDeviceServices -> rxBleDeviceServices.getBluetoothGattServices() } - .map { servicesList -> servicesList.size() } - } - .test() - - then: - testSubscriber.assertValue(1) - } - - def "should return characteristic data"() { - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUIDNoCallback) } - .map { data -> new String(data) } - .test() - - then: - testSubscriber.assertValue("Polidea") - } - - def "should return characteristic data via callback"() { - given: - nextCharacteristicReadWillResult { result -> result.success("PolideaCB".getBytes()) } - - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID) } - .map { data -> new String(data) } - .test() - - then: - testSubscriber.assertValue("PolideaCB") - } - - def "should throw characteristic read error via callback"() { - given: - nextCharacteristicReadWillResult { result -> result.failure(0x80) } - - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID) } - .map { data -> new String(data) } - .test() - - then: - testSubscriber.assertError { throwable -> - return BleGattCharacteristicException.class.isInstance(throwable) && ((BleGattCharacteristicException) throwable).getStatus() == 0x80 - } - } - - def "should throw characteristic read disconnection via callback"() { - given: - nextCharacteristicReadWillResult { result -> result.disconnect(0x80) } - - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID) } - .map { data -> new String(data) } - .test() - - then: - testSubscriber.assertError { throwable -> - return BleDisconnectedException.class.isInstance(throwable) && ((BleDisconnectedException) throwable).state == 0x80 - } - } - - def "should write characteristic data via callback"() { - given: - nextCharacteristicWriteWillResult { result -> result.success() } - - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.writeCharacteristic(characteristicUUID, "PolideaCB".getBytes()) } - .map { data -> new String(data) } - .test() - - then: - testSubscriber.assertValue("PolideaCB") - } - - def "should fail to write characteristic data via callback"() { - given: - nextCharacteristicWriteWillResult { result -> result.failure(0x80) } - - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.writeCharacteristic(characteristicUUID, "PolideaCB".getBytes()) } - .map { data -> new String(data) } - .test() - - then: - testSubscriber.assertError({ throwable -> - return BleGattCharacteristicException.class.isInstance(throwable) && ((BleGattCharacteristicException) throwable).getStatus() == 0x80 - }) - } - - def "should fail to write characteristic data due to disconnection via callback"() { - given: - nextCharacteristicWriteWillResult { result -> result.disconnect(0x80) } - - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.writeCharacteristic(characteristicUUID, "PolideaCB".getBytes()) } - .map { data -> new String(data) } - .test() - - then: - testSubscriber.assertError({ throwable -> - return BleDisconnectedException.class.isInstance(throwable) && ((BleDisconnectedException) throwable).state == 0x80 - }) - } - - def "should return descriptor data"() { - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.readDescriptor(serviceUUID, characteristicUUIDNoCallback, descriptorUUID) } - .map { data -> new String(data) } - .test() - - then: - testSubscriber.assertValue("Config") - } - - def "should return descriptor data via callback"() { - given: - nextDescriptorReadWillResult { result -> result.success("ConfigCB".getBytes()) } - - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.readDescriptor(serviceUUID, characteristicUUID, descriptorUUID) } - .map { data -> new String(data) } - .test() - - then: - testSubscriber.assertValue("ConfigCB") - } - - def "should throw descriptor read error via callback"() { - given: - nextDescriptorReadWillResult { result -> result.failure(0x80) } - - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.readDescriptor(serviceUUID, characteristicUUID, descriptorUUID) } - .map { data -> new String(data) } - .test() - - then: - testSubscriber.assertError({ throwable -> - return BleGattDescriptorException.class.isInstance(throwable) && ((BleGattDescriptorException) throwable).getStatus() == 0x80 - }) - } - - def "should throw descriptor read disconnection error via callback"() { - given: - nextDescriptorReadWillResult { result -> result.disconnect(0x80) } - - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMapSingle { rxBleConnection -> rxBleConnection.readDescriptor(serviceUUID, characteristicUUID, descriptorUUID) } - .map { data -> new String(data) } - .test() - - then: - testSubscriber.assertError({ throwable -> - return BleDisconnectedException.class.isInstance(throwable) && ((BleDisconnectedException) throwable).state == 0x80 - }) - } - - def "should write descriptor data via callback"() { - given: - nextDescriptorWriteWillResult { result -> result.success() } - - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .take(1) - .flatMapCompletable { rxBleConnection -> rxBleConnection.writeDescriptor(serviceUUID, characteristicUUID, descriptorUUID, "ConfigCB".getBytes()) } - .test() - - then: - testSubscriber.assertComplete(); - } - - def "should fail to write descriptor data via callback"() { - given: - nextDescriptorWriteWillResult { result -> result.failure(0x80) } - - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .take(1) - .flatMapCompletable { rxBleConnection -> rxBleConnection.writeDescriptor(serviceUUID, characteristicUUID, descriptorUUID, "ConfigCB".getBytes()) } - .test() - - then: - testSubscriber.assertError({ throwable -> - return BleGattDescriptorException.class.isInstance(throwable) && ((BleGattDescriptorException) throwable).getStatus() == 0x80 - }) - } - - def "should fail to write descriptor data via callback due to disconnection"() { - given: - nextDescriptorWriteWillResult { result -> result.disconnect(0x80) } - - when: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .take(1) - .flatMapCompletable { rxBleConnection -> rxBleConnection.writeDescriptor(serviceUUID, characteristicUUID, descriptorUUID, "ConfigCB".getBytes()) } - .test() - - then: - testSubscriber.assertError({ throwable -> - return BleDisconnectedException.class.isInstance(throwable) && ((BleDisconnectedException) throwable).state == 0x80 - }) - } - - def "should return notification data"() { - given: - def testSubscriber = rxBleClient.scanBleDevices(new ScanSettings.Builder().build()) - .take(1) - .map { scanResult -> scanResult.getBleDevice() } - .flatMap { rxBleDevice -> rxBleDevice.establishConnection(false) } - .flatMap { rxBleConnection -> rxBleConnection.setupNotification(characteristicNotifiedUUID) } - .flatMap({ Observable observable -> observable }) - .map { new String(it) } - .test() - - when: - characteristicNotificationSubject.onNext("NotificationData".getBytes()) - - then: - testSubscriber.assertValue("NotificationData") - } - def "should emit correct connection state values when connected"() { given: def device = rxBleClient.getBleDevice("AA:BB:CC:DD:EE:FF") diff --git a/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMockTest.groovy b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMockTest.groovy new file mode 100644 index 000000000..c017a7b15 --- /dev/null +++ b/mockrxandroidble/src/test/groovy/com/polidea/rxandroidble2/mockrxandroidble/RxBleConnectionMockTest.groovy @@ -0,0 +1,349 @@ +package com.polidea.rxandroidble2.mockrxandroidble + +import android.os.Build +import com.polidea.rxandroidble2.RxBleClient +import com.polidea.rxandroidble2.exceptions.BleDisconnectedException +import com.polidea.rxandroidble2.exceptions.BleGattCharacteristicException +import com.polidea.rxandroidble2.exceptions.BleGattDescriptorException +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattReadResultMock +import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattWriteResultMock +import hkhc.electricspock.ElectricSpecification +import io.reactivex.Observable +import io.reactivex.subjects.PublishSubject +import org.robolectric.annotation.Config + +@Config(manifest = Config.NONE, constants = BuildConfig, sdk = Build.VERSION_CODES.LOLLIPOP) +public class RxBleConnectionMockTest extends ElectricSpecification { + + def serviceUUID = UUID.fromString("00001234-0000-0000-8000-000000000000") + def characteristicUUID = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fb") + def characteristicUUIDNoCallback = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fc") + def characteristicNotifiedUUID = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fb") + def characteristicValue = "Polidea" + def characteristicData = characteristicValue.getBytes() + def descriptorUUID = UUID.fromString("00001337-0000-1000-8000-00805f9b34fb") + def descriptorValue = "Config" + def descriptorData = descriptorValue.getBytes() + RxBleClient rxBleClient + RxBleDeviceMock rxBleDeviceMock + RxBleConnectionMock rxBleConnectionMock + PublishSubject characteristicNotificationSubject = PublishSubject.create() + + PublishSubject characteristicReadSubject = PublishSubject.create() + PublishSubject characteristicWriteSubject = PublishSubject.create() + PublishSubject descriptorReadSubject = PublishSubject.create() + PublishSubject descriptorWriteSubject = PublishSubject.create() + + def nextCharacteristicReadWillResult(closure) { + characteristicReadSubject.take(1).subscribe(closure) + } + + def nextCharacteristicWriteWillResult(closure) { + characteristicWriteSubject.take(1).subscribe(closure) + } + + def nextDescriptorReadWillResult(closure) { + descriptorReadSubject.take(1).subscribe(closure) + } + + def nextDescriptorWriteWillResult(closure) { + descriptorWriteSubject.take(1).subscribe(closure) + } + + def createConnection(rssi) { + new RxBleConnectionMock.Builder() + .rssi(rssi) + .notificationSource(characteristicNotifiedUUID, characteristicNotificationSubject) + .addService(serviceUUID, + new RxBleClientMock.CharacteristicsBuilder() + .addCharacteristic( + characteristicUUID, + characteristicData, + new RxBleClientMock.DescriptorsBuilder() + .addDescriptor(descriptorUUID, descriptorData) + .build() + ).addCharacteristic( + characteristicUUIDNoCallback, + characteristicData, + new RxBleClientMock.DescriptorsBuilder() + .addDescriptor(descriptorUUID, descriptorData) + .build() + ).build() + ) + .characteristicReadCallback(characteristicUUID, { device, characteristic, result -> + characteristicReadSubject.onNext(result) + }) + .characteristicWriteCallback(characteristicUUID, { device, characteristic, bytes, result -> + characteristicWriteSubject.onNext(result) + }) + .descriptorReadCallback(characteristicUUID, descriptorUUID, { device, descriptor, result -> + descriptorReadSubject.onNext(result) + }) + .descriptorWriteCallback(characteristicUUID, descriptorUUID, { device, descriptor, bytes, result -> + descriptorWriteSubject.onNext(result) + }) + .build() + } + + def createDevice(macAddress, rssi) { + def connection = createConnection(rssi) + new Tuple2(new RxBleDeviceMock.Builder() + .deviceMacAddress(macAddress) + .scanRecord( + new RxBleScanRecordMock.Builder() + .setAdvertiseFlags(1) + .build() + ) + .connection(connection).build(), + connection) + } + + def setup() { + def (RxBleDeviceMock device, RxBleConnectionMock connection) = createDevice("AA:BB:CC:DD:EE:FF", 42) + rxBleClient = new RxBleClientMock.Builder().addDevice(device).build() + rxBleDeviceMock = device + rxBleConnectionMock = connection + } + + def "should return the BluetoothDevice mtu"() { + when: + def testSubscriber = rxBleConnectionMock + .requestMtu(72) + .test() + + then: + testSubscriber.assertValue(72) + } + + def "should return services list"() { + when: + def testSubscriber = rxBleConnectionMock + .discoverServices() + .map { rxBleDeviceServices -> rxBleDeviceServices.getBluetoothGattServices() } + .map { servicesList -> servicesList.size() } + .test() + + then: + testSubscriber.assertValue(1) + } + + def "should return characteristic data"() { + when: + def testSubscriber = rxBleConnectionMock + .readCharacteristic(characteristicUUIDNoCallback) + .map { data -> new String(data) } + .test() + + + then: + testSubscriber.assertValue(characteristicValue) + } + + def "should return characteristic data via callback"() { + given: + nextCharacteristicReadWillResult { result -> result.success(characteristicData) } + + when: + def testSubscriber = rxBleConnectionMock + .readCharacteristic(characteristicUUID) + .map { data -> new String(data) } + .test() + + then: + testSubscriber.assertValue(characteristicValue) + } + + def "should throw characteristic read error via callback"() { + given: + nextCharacteristicReadWillResult { result -> result.failure(0x80) } + + when: + def testSubscriber = rxBleConnectionMock + .readCharacteristic(characteristicUUID) + .map { data -> new String(data) } + .test() + + then: + testSubscriber.assertError { throwable -> + return BleGattCharacteristicException.class.isInstance(throwable) && ((BleGattCharacteristicException) throwable).getStatus() == 0x80 + } + } + + def "should throw characteristic read disconnection via callback"() { + given: + nextCharacteristicReadWillResult { result -> result.disconnect(0x80) } + + when: + def testSubscriber = rxBleConnectionMock + .readCharacteristic(characteristicUUID) + .map { data -> new String(data) } + .test() + + then: + testSubscriber.assertError { throwable -> + return BleDisconnectedException.class.isInstance(throwable) && ((BleDisconnectedException) throwable).state == 0x80 + } + } + + def "should write characteristic data via callback"() { + given: + nextCharacteristicWriteWillResult { result -> result.success() } + + when: + def testSubscriber = rxBleConnectionMock + .writeCharacteristic(characteristicUUID, characteristicData) + .map { data -> new String(data) } + .test() + + then: + testSubscriber.assertValue(characteristicValue) + } + + def "should fail to write characteristic data via callback"() { + given: + nextCharacteristicWriteWillResult { result -> result.failure(0x80) } + + when: + def testSubscriber = rxBleConnectionMock + .writeCharacteristic(characteristicUUID, characteristicData) + .map { data -> new String(data) } + .test() + + then: + testSubscriber.assertError({ throwable -> + return BleGattCharacteristicException.class.isInstance(throwable) && ((BleGattCharacteristicException) throwable).getStatus() == 0x80 + }) + } + + def "should fail to write characteristic data due to disconnection via callback"() { + given: + nextCharacteristicWriteWillResult { result -> result.disconnect(0x80) } + + when: + def testSubscriber = rxBleConnectionMock + .writeCharacteristic(characteristicUUID, characteristicData) + .map { data -> new String(data) } + .test() + + then: + testSubscriber.assertError({ throwable -> + return BleDisconnectedException.class.isInstance(throwable) && ((BleDisconnectedException) throwable).state == 0x80 + }) + } + + def "should return descriptor data"() { + when: + def testSubscriber = rxBleConnectionMock + .readDescriptor(serviceUUID, characteristicUUIDNoCallback, descriptorUUID) + .map { data -> new String(data) } + .test() + + then: + testSubscriber.assertValue(descriptorValue) + } + + def "should return descriptor data via callback"() { + given: + nextDescriptorReadWillResult { result -> result.success(descriptorData) } + + when: + def testSubscriber = rxBleConnectionMock + .readDescriptor(serviceUUID, characteristicUUID, descriptorUUID) + .map { data -> new String(data) } + .test() + + then: + testSubscriber.assertValue(descriptorValue) + } + + def "should throw descriptor read error via callback"() { + given: + nextDescriptorReadWillResult { result -> result.failure(0x80) } + + when: + def testSubscriber = rxBleConnectionMock + .readDescriptor(serviceUUID, characteristicUUID, descriptorUUID) + .map { data -> new String(data) } + .test() + + then: + testSubscriber.assertError({ throwable -> + return BleGattDescriptorException.class.isInstance(throwable) && ((BleGattDescriptorException) throwable).getStatus() == 0x80 + }) + } + + def "should throw descriptor read disconnection error via callback"() { + given: + nextDescriptorReadWillResult { result -> result.disconnect(0x80) } + + when: + def testSubscriber = rxBleConnectionMock + .readDescriptor(serviceUUID, characteristicUUID, descriptorUUID) + .map { data -> new String(data) } + .test() + + then: + testSubscriber.assertError({ throwable -> + return BleDisconnectedException.class.isInstance(throwable) && ((BleDisconnectedException) throwable).state == 0x80 + }) + } + + def "should write descriptor data via callback"() { + given: + nextDescriptorWriteWillResult { result -> result.success() } + + when: + def testSubscriber = rxBleConnectionMock + .writeDescriptor(serviceUUID, characteristicUUID, descriptorUUID, descriptorData) + .test() + + then: + testSubscriber.assertComplete(); + } + + def "should fail to write descriptor data via callback"() { + given: + nextDescriptorWriteWillResult { result -> result.failure(0x80) } + + when: + def testSubscriber = rxBleConnectionMock + .writeDescriptor(serviceUUID, characteristicUUID, descriptorUUID, descriptorData) + .test() + + then: + testSubscriber.assertError({ throwable -> + return BleGattDescriptorException.class.isInstance(throwable) && ((BleGattDescriptorException) throwable).getStatus() == 0x80 + }) + } + + def "should fail to write descriptor data via callback due to disconnection"() { + given: + nextDescriptorWriteWillResult { result -> result.disconnect(0x80) } + + when: + def testSubscriber = rxBleConnectionMock + .writeDescriptor(serviceUUID, characteristicUUID, descriptorUUID, descriptorData) + .test() + + then: + testSubscriber.assertError({ throwable -> + return BleDisconnectedException.class.isInstance(throwable) && ((BleDisconnectedException) throwable).state == 0x80 + }) + } + + def "should return notification data"() { + given: + def testSubscriber = rxBleConnectionMock + .setupNotification(characteristicNotifiedUUID) + .flatMap({ Observable observable -> observable }) + .map { new String(it) } + .test() + + when: + characteristicNotificationSubject.onNext("NotificationData".getBytes()) + + then: + testSubscriber.assertValue("NotificationData") + } + +}