diff --git a/CHANGELOG.md b/CHANGELOG.md index 2728ee14..27e33b80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.9 + +* Fixed issue with incorrectly typed Stream + ## 2.2.8 * Formatted all sources according to dartfmt for consistency diff --git a/android/build.gradle b/android/build.gradle index 3b20cada..a1875ce4 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ group 'com.polidea.flutter_ble_lib' -version '2.2.8' +version '2.2.9' buildscript { repositories { diff --git a/ios/flutter_ble_lib.podspec b/ios/flutter_ble_lib.podspec index a5eeae54..2fe4a3c8 100644 --- a/ios/flutter_ble_lib.podspec +++ b/ios/flutter_ble_lib.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_ble_lib' - s.version = '2.2.8' + s.version = '2.2.9' s.summary = 'A new flutter plugin project.' s.description = <<-DESC A new flutter plugin project. diff --git a/lib/src/bridge/device_connection_mixin.dart b/lib/src/bridge/device_connection_mixin.dart index 5b28044c..f8a9061a 100644 --- a/lib/src/bridge/device_connection_mixin.dart +++ b/lib/src/bridge/device_connection_mixin.dart @@ -25,7 +25,7 @@ mixin DeviceConnectionMixin on FlutterBLE { Stream observePeripheralConnectionState( String identifier, bool emitCurrentValue) { - var controller = StreamController( + var controller = StreamController( onListen: () => _methodChannel.invokeMethod( MethodName.observeConnectionState, { @@ -37,18 +37,15 @@ mixin DeviceConnectionMixin on FlutterBLE { ), ); - controller - .addStream(_peripheralConnectionStateChanges) - .then((value) => controller?.close()); - - return controller.stream - .map((jsonString) => - ConnectionStateContainer.fromJson(jsonDecode(jsonString))) - .where((connectionStateContainer) => - connectionStateContainer.peripheralIdentifier == identifier) - .map((connectionStateContainer) => - connectionStateContainer.connectionState) - .map((connectionStateString) { + var sourceStream = + _peripheralConnectionStateChanges + .map((jsonString) => + ConnectionStateContainer.fromJson(jsonDecode(jsonString))) + .where((connectionStateContainer) => + connectionStateContainer.peripheralIdentifier == identifier) + .map((connectionStateContainer) => + connectionStateContainer.connectionState) + .map((connectionStateString) { switch (connectionStateString.toLowerCase()) { case NativeConnectionState.connected: return PeripheralConnectionState.connected; @@ -64,6 +61,15 @@ mixin DeviceConnectionMixin on FlutterBLE { ); } }); + + controller + .addStream( + sourceStream, + cancelOnError: true, + ) + .then((value) => controller?.close()); + + return controller.stream; } Future isPeripheralConnected(String peripheralIdentifier) async { diff --git a/pubspec.yaml b/pubspec.yaml index e6463a63..5c5565b4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_ble_lib description: FlutterBle Library is a flutter library that supports BLE operations. It uses MultiPlatformBleAdapter as a native backend.. -version: 2.2.8 +version: 2.2.9 homepage: https://github.com/Polidea/FlutterBleLib environment: diff --git a/test/characteristic_test.dart b/test/characteristic_test.dart new file mode 100644 index 00000000..543e0a13 --- /dev/null +++ b/test/characteristic_test.dart @@ -0,0 +1,355 @@ +import 'dart:async'; +import 'dart:developer'; +import 'dart:typed_data'; + +import 'package:flutter_ble_lib/flutter_ble_lib.dart'; +import 'package:flutter_ble_lib/src/_managers_for_classes.dart'; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; + +import 'mock/mocks.dart'; +import 'test_util/characteristic_generator.dart'; +import 'test_util/descriptor_generator.dart'; + +class ServiceMock extends Mock implements Service {} + +void main() { + Peripheral peripheral = PeripheralMock(); + ManagerForCharacteristic managerForCharacteristic = + ManagerForCharacteristicMock(); + CharacteristicGenerator characteristicGenerator = + CharacteristicGenerator(managerForCharacteristic); + DescriptorGenerator descriptorGenerator = + DescriptorGenerator(ManagerForDescriptorMock()); + + Characteristic characteristic = + characteristicGenerator.create(123, ServiceMock()); + + DescriptorWithValue createDescriptor(int seed) => + descriptorGenerator.create(seed, characteristic); + + tearDown(() { + [ + peripheral, + managerForCharacteristic, + ].forEach(clearInteractions); + }); + + test("descriptors returns a list of descriptors provided by manager", () async { + //given + when(managerForCharacteristic.descriptorsForCharacteristic(characteristic)) + .thenAnswer((_) => Future.value([ + createDescriptor(0), + createDescriptor(1), + createDescriptor(2), + ])); + + //when + var descriptors = await characteristic.descriptors(); + + //then + expect( + descriptors, + equals([ + createDescriptor(0), + createDescriptor(1), + createDescriptor(2), + ])); + }); + + test("read returns expected value", () async { + //given + when(managerForCharacteristic.readCharacteristicForIdentifier( + any, characteristic, "a123")) + .thenAnswer((_) => Future.value(Uint8List.fromList([1, 2, 3, 4]))); + + //when + var value = await characteristic.read(transactionId: "a123"); + + //then + expect(value, equals(Uint8List.fromList([1, 2, 3, 4]))); + }); + + test( + "read invokes manager with expected params when transactionId is specified", + () { + //when + characteristic.read(transactionId: "a123"); + + //then + verify( + managerForCharacteristic.readCharacteristicForIdentifier( + any, characteristic, "a123"), + ); + }); + + test( + "read generates transactionId when it is not specified", + () { + //when + characteristic.read(); + + //then + verify( + managerForCharacteristic.readCharacteristicForIdentifier( + any, characteristic, argThat(isNotNull)), + ); + }); + + test( + "read generates unique transactionId for each operation", + () { + //when + characteristic.read(); + characteristic.read(); + + //then + var transactionIds = verify( + managerForCharacteristic.readCharacteristicForIdentifier( + any, characteristic, captureThat(isNotNull)), + ).captured; + expect(transactionIds[0], isNot(equals(transactionIds[1]))); + }); + + test( + "write invokes manager with expected params when transactionId is specified", + () { + //when + characteristic.write( + Uint8List.fromList([1, 2, 3, 4]), + false, + transactionId: "a456", + ); + + //then + verify( + managerForCharacteristic.writeCharacteristicForIdentifier( + any, characteristic, Uint8List.fromList([1, 2, 3, 4]), false, "a456"), + ); + }); + + test( + "write invokes manager with expected params when transactionId is not specified", + () { + //when + characteristic.write(Uint8List.fromList([1, 2, 3, 4]), false); + + //then + verify( + managerForCharacteristic.writeCharacteristicForIdentifier( + any, + characteristic, + Uint8List.fromList([1, 2, 3, 4]), + false, + argThat(isNotNull)), + ); + }); + + test( + "write invokes manager with unique transactionId when transactionId is not specified", + () { + //when + characteristic.write(Uint8List.fromList([1, 2, 3, 4]), false); + characteristic.write(Uint8List.fromList([1, 2, 3, 4]), false); + + //then + var transactionIds = verify( + managerForCharacteristic.writeCharacteristicForIdentifier( + any, + characteristic, + Uint8List.fromList([1, 2, 3, 4]), + false, + captureThat(isNotNull))) + .captured; + expect(transactionIds[0], isNot(equals(transactionIds[1]))); + }); + + test("monitor emits expected values", () { + //given + var streamController = StreamController(); + when(managerForCharacteristic.monitorCharacteristicForIdentifier( + any, characteristic, "a123")) + .thenAnswer((_) => streamController.stream); + + //when + var valuesNotifications = characteristic.monitor(transactionId: "a123"); + streamController.sink.add(Uint8List.fromList([1, 2, 3])); + streamController.sink.add(Uint8List.fromList([4, 5, 6])); + streamController.sink.add(Uint8List.fromList([7, 8, 9])); + streamController.close(); + + //then + expect( + valuesNotifications, + emitsInOrder([ + emits(equals(Uint8List.fromList([1, 2, 3]))), + emits(equals(Uint8List.fromList([4, 5, 6]))), + emits(equals(Uint8List.fromList([7, 8, 9]))), + emitsDone + ])); + }); + + test( + "monitor invokes manager with expected params when transactionId is specified", + () { + //when + characteristic.monitor(transactionId: "a123"); + + //then + verify( + managerForCharacteristic.monitorCharacteristicForIdentifier( + any, characteristic, "a123"), + ); + }); + + test( + "monitor invokes manager with expected params when transactionId is not specified", + () { + //when + characteristic.monitor(); + + //then + verify( + managerForCharacteristic.monitorCharacteristicForIdentifier( + any, characteristic, argThat(isNotNull)), + ); + }); + + test( + "monitor invokes manager with unique transactionId when transactionId is not specified", + () { + //when + characteristic.monitor(); + characteristic.monitor(); + + //then + var transactionIds = verify( + managerForCharacteristic.monitorCharacteristicForIdentifier( + any, characteristic, captureThat(isNotNull))) + .captured; + expect(transactionIds[0], isNot(equals(transactionIds[1]))); + }); + + test("readDescriptor returns expected descriptor", () async { + //given + when(managerForCharacteristic.readDescriptorForCharacteristic( + characteristic, "123", "a456")) + .thenAnswer((_) => Future.value(createDescriptor(0))); + + //when + var descriptor = + await characteristic.readDescriptor("123", transactionId: "a456"); + + //then + expect(descriptor, equals(createDescriptor(0))); + }); + + test( + "readDescriptor invokes manager with expected params when transactionId is specified", + () { + //when + characteristic.readDescriptor("123", transactionId: "a456"); + + //then + verify( + managerForCharacteristic.readDescriptorForCharacteristic( + characteristic, "123", "a456"), + ); + }); + + test( + "readDescriptor invokes manager with expected params when transactionId is not specified", + () { + //when + characteristic.readDescriptor("123", transactionId: "a456"); + + //then + verify( + managerForCharacteristic.readDescriptorForCharacteristic( + characteristic, "123", argThat(isNotNull)), + ); + }); + + test( + "readDescriptor invokes manager with unique transactionId when transactionId is not specified", + () { + //when + characteristic.readDescriptor("123"); + characteristic.readDescriptor("123"); + + //then + var transactionIds = verify( + managerForCharacteristic.readDescriptorForCharacteristic( + characteristic, "123", captureThat(isNotNull)), + ).captured; + expect(transactionIds[0], isNot(equals(transactionIds[1]))); + }); + + test("writeDescriptor returns expected descriptor", () async { + //given + when(managerForCharacteristic.writeDescriptorForCharacteristic( + characteristic, "123", Uint8List.fromList([1, 2, 3, 4]), "a456")) + .thenAnswer((_) => Future.value(createDescriptor(0))); + + //when + var descriptor = await characteristic.writeDescriptor( + "123", + Uint8List.fromList([1, 2, 3, 4]), + transactionId: "a456", + ); + + //then + expect(descriptor, equals(createDescriptor(0))); + }); + + test( + "writeDescriptor invokes manager with expected params when transactionId is specified", + () { + //when + characteristic.writeDescriptor( + "123", + Uint8List.fromList([1, 2, 3, 4]), + transactionId: "a456", + ); + + //then + verify( + managerForCharacteristic.writeDescriptorForCharacteristic( + characteristic, "123", Uint8List.fromList([1, 2, 3, 4]), "a456"), + ); + }); + + test( + "writeDescriptor invokes manager with expected params when transactionId is not specified", + () { + //when + characteristic.writeDescriptor("123", Uint8List.fromList([1, 2, 3, 4])); + //then + verify( + managerForCharacteristic.writeDescriptorForCharacteristic( + characteristic, + "123", + Uint8List.fromList([1, 2, 3, 4]), + argThat(isNotNull), + ), + ); + }); + + test( + "writeDescriptor invokes manager with unique transactionId when transactionId is not specified", + () { + //when + characteristic.writeDescriptor("123", Uint8List.fromList([1, 2, 3, 4])); + characteristic.writeDescriptor("123", Uint8List.fromList([1, 2, 3, 4])); + + //then + var transactionIds = verify( + managerForCharacteristic.writeDescriptorForCharacteristic( + characteristic, + "123", + Uint8List.fromList([1, 2, 3, 4]), + captureThat(isNotNull)), + ).captured; + expect(transactionIds[0], isNot(equals(transactionIds[1]))); + }); +} diff --git a/test/service_test.dart b/test/service_test.dart index 1c70e155..4bbf0fa7 100644 --- a/test/service_test.dart +++ b/test/service_test.dart @@ -90,12 +90,12 @@ void main() { test("readCharacteristic returns expected characteristic", () async { //given when(managerForService.readCharacteristicForService( - peripheral, service, "123", "456")) + peripheral, service, "123", "a456")) .thenAnswer((_) => Future.value(createCharacteristic(0))); //when var characteristic = - await service.readCharacteristic("123", transactionId: "456"); + await service.readCharacteristic("123", transactionId: "a456"); //then expect(characteristic, equals(createCharacteristic(0))); @@ -103,12 +103,12 @@ void main() { test("readCharacteristic reads characteristic using manager", () { //when - service.readCharacteristic("123", transactionId: "456"); + service.readCharacteristic("123", transactionId: "a456"); //then verify( managerForService.readCharacteristicForService( - peripheral, service, "123", "456"), + peripheral, service, "123", "a456"), ); }); @@ -128,24 +128,24 @@ void main() { test("readDescriptor returns expected descriptor", () async { //given when(managerForService.readDescriptorForService( - service, "123", "456", "789")) + service, "123", "456", "a789")) .thenAnswer((_) => Future.value(createDescriptor(0))); //when var characteristic = - await service.readDescriptor("123", "456", transactionId: "789"); + await service.readDescriptor("123", "456", transactionId: "a789"); //then expect(characteristic, equals(createDescriptor(0))); }); - test("readDescriptor reads characteristic using manager", () { + test("readDescriptor reads descriptor using manager", () { //when - service.readDescriptor("123", "456", transactionId: "789"); + service.readDescriptor("123", "456", transactionId: "a789"); //then verify( - managerForService.readDescriptorForService(service, "123", "456", "789"), + managerForService.readDescriptorForService(service, "123", "456", "a789"), ); }); @@ -165,7 +165,7 @@ void main() { test("writeCharacteristic returns expected characteristic", () async { //given when(managerForService.writeCharacteristicForService(peripheral, service, - "123", Uint8List.fromList([1, 2, 3, 4]), false, "456")) + "123", Uint8List.fromList([1, 2, 3, 4]), false, "a456")) .thenAnswer((_) => Future.value(createCharacteristic(0))); //when @@ -173,7 +173,7 @@ void main() { "123", Uint8List.fromList([1, 2, 3, 4]), false, - transactionId: "456", + transactionId: "a456", ); //then @@ -183,12 +183,12 @@ void main() { test("writeCharacteristic writes characteristic using manager", () { //when service.writeCharacteristic("123", Uint8List.fromList([1, 2, 3, 4]), false, - transactionId: "456"); + transactionId: "a456"); //then verify( managerForService.writeCharacteristicForService(peripheral, service, - "123", Uint8List.fromList([1, 2, 3, 4]), false, "456"), + "123", Uint8List.fromList([1, 2, 3, 4]), false, "a456"), ); }); @@ -209,27 +209,27 @@ void main() { test("writeDescriptor returns expected descriptor", () async { //given when(managerForService.writeDescriptorForService( - service, "123", "456", Uint8List.fromList([1, 2, 3, 4]), "789")) + service, "123", "456", Uint8List.fromList([1, 2, 3, 4]), "a789")) .thenAnswer((_) => Future.value(createDescriptor(0))); //when - var characteristic = await service.writeDescriptor( + var descriptor = await service.writeDescriptor( "123", "456", Uint8List.fromList([1, 2, 3, 4]), - transactionId: "789"); + transactionId: "a789"); //then - expect(characteristic, equals(createDescriptor(0))); + expect(descriptor, equals(createDescriptor(0))); }); test("writeDescriptor writes descriptor using manager", () { //when service.writeDescriptor("123", "456", Uint8List.fromList([1, 2, 3, 4]), - transactionId: "789"); + transactionId: "a789"); //then verify( managerForService.writeDescriptorForService( - service, "123", "456", Uint8List.fromList([1, 2, 3, 4]), "789"), + service, "123", "456", Uint8List.fromList([1, 2, 3, 4]), "a789"), ); }); @@ -251,12 +251,12 @@ void main() { //given var streamController = StreamController(); when(managerForService.monitorCharacteristicForService( - peripheral, service, "123", "456")) + peripheral, service, "123", "a456")) .thenAnswer((_) => streamController.stream); //when var characteristicNotifications = - service.monitorCharacteristic("123", transactionId: "456"); + service.monitorCharacteristic("123", transactionId: "a456"); streamController.sink.add(createCharacteristic(0)); streamController.sink.add(createCharacteristic(1)); streamController.sink.add(createCharacteristic(2)); @@ -275,12 +275,12 @@ void main() { test("monitorCharacteristic monitors characteristic using manager", () { //when - service.monitorCharacteristic("123", transactionId: "456"); + service.monitorCharacteristic("123", transactionId: "a456"); //then verify( managerForService.monitorCharacteristicForService( - peripheral, service, "123", "456"), + peripheral, service, "123", "a456"), ); });