diff --git a/lib/flutter_foreground_task.dart b/lib/flutter_foreground_task.dart index d82e2cf1..d7cd7fc2 100644 --- a/lib/flutter_foreground_task.dart +++ b/lib/flutter_foreground_task.dart @@ -58,6 +58,9 @@ class FlutterForegroundTask { @visibleForTesting static bool isInitialized = false; + @visibleForTesting + static bool skipServiceResponseCheck = false; + // platform instance: MethodChannelFlutterForegroundTask static FlutterForegroundTaskPlatform get _platform => FlutterForegroundTaskPlatform.instance; @@ -69,6 +72,7 @@ class FlutterForegroundTask { iosNotificationOptions = null; foregroundTaskOptions = null; isInitialized = false; + skipServiceResponseCheck = false; receivePort?.close(); receivePort = null; @@ -120,24 +124,26 @@ class FlutterForegroundTask { callback: callback, ); - final Stopwatch stopwatch = Stopwatch()..start(); - bool isStarted = false; - await Future.doWhile(() async { - isStarted = await isRunningService; - - // official doc: Once the service has been created, the service must call its startForeground() method within five seconds. - // ref: https://developer.android.com/guide/components/services#StartingAService - if (isStarted || stopwatch.elapsedMilliseconds > 5 * 1000) { - return false; - } else { - await Future.delayed(const Duration(milliseconds: 100)); - return true; + if (!skipServiceResponseCheck) { + final Stopwatch stopwatch = Stopwatch()..start(); + bool isStarted = false; + await Future.doWhile(() async { + isStarted = await isRunningService; + + // official doc: Once the service has been created, the service must call its startForeground() method within five seconds. + // ref: https://developer.android.com/guide/components/services#StartingAService + if (isStarted || stopwatch.elapsedMilliseconds > 5 * 1000) { + return false; + } else { + await Future.delayed(const Duration(milliseconds: 100)); + return true; + } + }); + + // no response :( + if (!isStarted) { + throw ServiceTimeoutException(); } - }); - - // no response :( - if (!isStarted) { - throw ServiceTimeoutException(); } return ServiceRequestResult.success(); @@ -199,24 +205,26 @@ class FlutterForegroundTask { await _platform.stopService(); - final Stopwatch stopwatch = Stopwatch()..start(); - bool isStopped = false; - await Future.doWhile(() async { - isStopped = !(await isRunningService); - - // official doc: Once the service has been created, the service must call its startForeground() method within five seconds. - // ref: https://developer.android.com/guide/components/services#StartingAService - if (isStopped || stopwatch.elapsedMilliseconds > 5 * 1000) { - return false; - } else { - await Future.delayed(const Duration(milliseconds: 100)); - return true; + if (!skipServiceResponseCheck) { + final Stopwatch stopwatch = Stopwatch()..start(); + bool isStopped = false; + await Future.doWhile(() async { + isStopped = !(await isRunningService); + + // official doc: Once the service has been created, the service must call its startForeground() method within five seconds. + // ref: https://developer.android.com/guide/components/services#StartingAService + if (isStopped || stopwatch.elapsedMilliseconds > 5 * 1000) { + return false; + } else { + await Future.delayed(const Duration(milliseconds: 100)); + return true; + } + }); + + // no response :( + if (!isStopped) { + throw ServiceTimeoutException(); } - }); - - // no response :( - if (!isStopped) { - throw ServiceTimeoutException(); } return ServiceRequestResult.success(); diff --git a/test/dummy/service_dummy_data.dart b/test/dummy/service_dummy_data.dart index d5e287bf..bfac5945 100644 --- a/test/dummy/service_dummy_data.dart +++ b/test/dummy/service_dummy_data.dart @@ -1,5 +1,13 @@ +import 'dart:ui'; + import 'package:flutter/material.dart'; import 'package:flutter_foreground_task/flutter_foreground_task.dart'; +import 'package:platform/platform.dart'; + +@pragma('vm:entry-point') +void testCallback() { + print('test'); +} class ServiceDummyData { final AndroidNotificationOptions androidNotificationOptions = @@ -49,4 +57,33 @@ class ServiceDummyData { const NotificationButton( id: 'id_test2', text: 'test2', textColor: Colors.green), ]; + + Map getStartServiceArgs(String platform) { + return { + 'serviceId': serviceId, + if (platform == Platform.android) + ...androidNotificationOptions.toJson() + else if (platform == Platform.iOS) + ...iosNotificationOptions.toJson(), + ...foregroundTaskOptions.toJson(), + 'notificationContentTitle': notificationTitle, + 'notificationContentText': notificationText, + 'iconData': notificationIcon.toJson(), + 'buttons': notificationButtons.map((e) => e.toJson()).toList(), + 'callbackHandle': + PluginUtilities.getCallbackHandle(testCallback)?.toRawHandle(), + }; + } + + Map getUpdateServiceArgs() { + return { + ...foregroundTaskOptions.toJson(), + 'notificationContentTitle': notificationTitle, + 'notificationContentText': notificationText, + 'iconData': notificationIcon.toJson(), + 'buttons': notificationButtons.map((e) => e.toJson()).toList(), + 'callbackHandle': + PluginUtilities.getCallbackHandle(testCallback)?.toRawHandle(), + }; + } } diff --git a/test/service_api_test.dart b/test/service_api_test.dart index 901fe12a..1a180b28 100644 --- a/test/service_api_test.dart +++ b/test/service_api_test.dart @@ -1,7 +1,10 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_foreground_task/flutter_foreground_task.dart'; +import 'package:flutter_foreground_task/flutter_foreground_task_method_channel.dart'; import 'package:flutter_foreground_task/flutter_foreground_task_platform_interface.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:platform/platform.dart'; import 'dummy/service_dummy_data.dart'; @@ -10,155 +13,468 @@ void main() { final ServiceDummyData dummyData = ServiceDummyData(); - late MockFlutterForegroundTask mock; + late MethodChannelFlutterForegroundTask platformChannel; + late ServiceApiMethodCallHandler methodCallHandler; setUp(() { - mock = MockFlutterForegroundTask(); - FlutterForegroundTaskPlatform.instance = mock; + platformChannel = MethodChannelFlutterForegroundTask(); + FlutterForegroundTaskPlatform.instance = platformChannel; FlutterForegroundTask.resetStatic(); - }); - - test('init', () { - expect(FlutterForegroundTask.isInitialized, false); - _init(dummyData); + methodCallHandler = + ServiceApiMethodCallHandler(() => platformChannel.platform); - expect(FlutterForegroundTask.isInitialized, true); - expect( - FlutterForegroundTask.androidNotificationOptions, - dummyData.androidNotificationOptions, - ); - expect( - FlutterForegroundTask.iosNotificationOptions, - dummyData.iosNotificationOptions, + // method channel + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler( + platformChannel.mMDChannel, + methodCallHandler.onMethodCall, ); - expect( - FlutterForegroundTask.foregroundTaskOptions, - dummyData.foregroundTaskOptions, - ); - }); - - test('startService', () async { - _init(dummyData); - - final ServiceRequestResult result = await _startService(dummyData); - expect(result.success, true); - expect(result.error, isNull); - }); - - test('startService (error: ServiceNotInitializedException)', () async { - final ServiceRequestResult result = await _startService(dummyData); - expect(result.success, false); - expect(result.error, isA()); }); - test('startService (error: ServiceAlreadyStartedException)', () async { - _init(dummyData); - - final ServiceRequestResult result1 = await _startService(dummyData); - expect(result1.success, true); - expect(result1.error, isNull); - - final ServiceRequestResult result2 = await _startService(dummyData); - expect(result2.success, false); - expect(result2.error, isA()); + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(platformChannel.mMDChannel, null); }); - test('startService (error: ServiceTimeoutException)', () async { - _init(dummyData); - - // set timeoutTest - mock.timeoutTest = true; - - final ServiceRequestResult result = await _startService(dummyData); - expect(result.success, false); - expect(result.error, isA()); - }); - - test('restartService', () async { - _init(dummyData); - - final ServiceRequestResult result1 = await _startService(dummyData); - expect(result1.success, true); - expect(result1.error, isNull); - - final ServiceRequestResult result2 = await _restartService(); - expect(result2.success, true); - expect(result2.error, isNull); - }); - - test('restartService (error: ServiceNotStartedException)', () async { - final ServiceRequestResult result = await _restartService(); - expect(result.success, false); - expect(result.error, isA()); - }); - - test('updateService', () async { - _init(dummyData); - - final ServiceRequestResult result1 = await _startService(dummyData); - expect(result1.success, true); - expect(result1.error, isNull); - - final ServiceRequestResult result2 = await _updateService(dummyData); - expect(result2.success, true); - expect(result2.error, isNull); - }); - - test('updateService (error: ServiceNotStartedException)', () async { - final ServiceRequestResult result = await _updateService(dummyData); - expect(result.success, false); - expect(result.error, isA()); - }); - - test('stopService', () async { - _init(dummyData); - - final ServiceRequestResult result1 = await _startService(dummyData); - expect(result1.success, true); - expect(result1.error, isNull); - - final ServiceRequestResult result2 = await _stopService(); - expect(result2.success, true); - expect(result2.error, isNull); - }); - - test('stopService (error: ServiceNotStartedException)', () async { - final ServiceRequestResult result = await _stopService(); - expect(result.success, false); - expect(result.error, isA()); - }); - - test('stopService (error: ServiceTimeoutException)', () async { - _init(dummyData); - - final ServiceRequestResult result1 = await _startService(dummyData); - expect(result1.success, true); - expect(result1.error, isNull); - - // set timeoutTest - mock.timeoutTest = true; - - final ServiceRequestResult result2 = await _stopService(); - expect(result2.success, false); - expect(result2.error, isA()); + group('Android', () { + const String platform = Platform.android; + + test('init', () { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + expect(FlutterForegroundTask.isInitialized, false); + expect(FlutterForegroundTask.androidNotificationOptions, isNull); + expect(FlutterForegroundTask.iosNotificationOptions, isNull); + expect(FlutterForegroundTask.foregroundTaskOptions, isNull); + + _init(dummyData); + + expect(FlutterForegroundTask.isInitialized, true); + expect( + FlutterForegroundTask.androidNotificationOptions, + dummyData.androidNotificationOptions, + ); + expect( + FlutterForegroundTask.iosNotificationOptions, + dummyData.iosNotificationOptions, + ); + expect( + FlutterForegroundTask.foregroundTaskOptions, + dummyData.foregroundTaskOptions, + ); + }); + + test('startService', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + FlutterForegroundTask.skipServiceResponseCheck = true; + + _init(dummyData); + + final ServiceRequestResult result = await _startService(dummyData); + expect(result.success, true); + expect(result.error, isNull); + expect( + methodCallHandler.log.last, + isMethodCall( + ServiceApiMethod.startService, + arguments: dummyData.getStartServiceArgs(platform), + ), + ); + }); + + test('startService (error: ServiceNotInitializedException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + final ServiceRequestResult result = await _startService(dummyData); + expect(result.success, false); + expect(result.error, isA()); + }); + + test('startService (error: ServiceAlreadyStartedException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + _init(dummyData); + + final ServiceRequestResult result1 = await _startService(dummyData); + expect(result1.success, true); + expect(result1.error, isNull); + + final ServiceRequestResult result2 = await _startService(dummyData); + expect(result2.success, false); + expect(result2.error, isA()); + }); + + test('startService (error: ServiceTimeoutException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + _init(dummyData); + + // set test + methodCallHandler.timeoutTest = true; + + final ServiceRequestResult result = await _startService(dummyData); + expect(result.success, false); + expect(result.error, isA()); + }); + + test('restartService', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + _init(dummyData); + + final ServiceRequestResult result1 = await _startService(dummyData); + expect(result1.success, true); + expect(result1.error, isNull); + + final ServiceRequestResult result2 = await _restartService(); + expect(result2.success, true); + expect(result2.error, isNull); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.restartService, arguments: null), + ); + }); + + test('restartService (error: ServiceNotStartedException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + final ServiceRequestResult result = await _restartService(); + expect(result.success, false); + expect(result.error, isA()); + }); + + test('updateService', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + _init(dummyData); + + final ServiceRequestResult result1 = await _startService(dummyData); + expect(result1.success, true); + expect(result1.error, isNull); + + final ServiceRequestResult result2 = await _updateService(dummyData); + expect(result2.success, true); + expect(result2.error, isNull); + expect( + methodCallHandler.log.last, + isMethodCall( + ServiceApiMethod.updateService, + arguments: dummyData.getUpdateServiceArgs(), + ), + ); + }); + + test('updateService (error: ServiceNotStartedException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + final ServiceRequestResult result = await _updateService(dummyData); + expect(result.success, false); + expect(result.error, isA()); + }); + + test('stopService', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + FlutterForegroundTask.skipServiceResponseCheck = true; + + _init(dummyData); + + final ServiceRequestResult result1 = await _startService(dummyData); + expect(result1.success, true); + expect(result1.error, isNull); + + final ServiceRequestResult result2 = await _stopService(); + expect(result2.success, true); + expect(result2.error, isNull); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.stopService, arguments: null), + ); + }); + + test('stopService (error: ServiceNotStartedException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + final ServiceRequestResult result = await _stopService(); + expect(result.success, false); + expect(result.error, isA()); + }); + + test('stopService (error: ServiceTimeoutException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + _init(dummyData); + + final ServiceRequestResult result1 = await _startService(dummyData); + expect(result1.success, true); + expect(result1.error, isNull); + + // set test + methodCallHandler.timeoutTest = true; + + final ServiceRequestResult result2 = await _stopService(); + expect(result2.success, false); + expect(result2.error, isA()); + }); + + test('isRunningService', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + _init(dummyData); + expect(await _isRunningService, false); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.isRunningService, arguments: null), + ); + + await _startService(dummyData); + expect(await _isRunningService, true); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.isRunningService, arguments: null), + ); + + await _restartService(); + expect(await _isRunningService, true); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.isRunningService, arguments: null), + ); + + await _updateService(dummyData); + expect(await _isRunningService, true); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.isRunningService, arguments: null), + ); + + await _stopService(); + expect(await _isRunningService, false); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.isRunningService, arguments: null), + ); + }); }); - test('isRunningService', () async { - _init(dummyData); - expect(await _isRunningService, false); - - await _startService(dummyData); - expect(await _isRunningService, true); - - await _restartService(); - expect(await _isRunningService, true); - - await _updateService(dummyData); - expect(await _isRunningService, true); - - await _stopService(); - expect(await _isRunningService, false); + group('iOS', () { + const String platform = Platform.iOS; + + test('init', () { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + expect(FlutterForegroundTask.isInitialized, false); + expect(FlutterForegroundTask.androidNotificationOptions, isNull); + expect(FlutterForegroundTask.iosNotificationOptions, isNull); + expect(FlutterForegroundTask.foregroundTaskOptions, isNull); + + _init(dummyData); + + expect(FlutterForegroundTask.isInitialized, true); + expect( + FlutterForegroundTask.androidNotificationOptions, + dummyData.androidNotificationOptions, + ); + expect( + FlutterForegroundTask.iosNotificationOptions, + dummyData.iosNotificationOptions, + ); + expect( + FlutterForegroundTask.foregroundTaskOptions, + dummyData.foregroundTaskOptions, + ); + }); + + test('startService', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + FlutterForegroundTask.skipServiceResponseCheck = true; + + _init(dummyData); + + final ServiceRequestResult result = await _startService(dummyData); + expect(result.success, true); + expect(result.error, isNull); + expect( + methodCallHandler.log.last, + isMethodCall( + ServiceApiMethod.startService, + arguments: dummyData.getStartServiceArgs(platform), + ), + ); + }); + + test('startService (error: ServiceNotInitializedException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + final ServiceRequestResult result = await _startService(dummyData); + expect(result.success, false); + expect(result.error, isA()); + }); + + test('startService (error: ServiceAlreadyStartedException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + _init(dummyData); + + final ServiceRequestResult result1 = await _startService(dummyData); + expect(result1.success, true); + expect(result1.error, isNull); + + final ServiceRequestResult result2 = await _startService(dummyData); + expect(result2.success, false); + expect(result2.error, isA()); + }); + + test('startService (error: ServiceTimeoutException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + _init(dummyData); + + // set test + methodCallHandler.timeoutTest = true; + + final ServiceRequestResult result = await _startService(dummyData); + expect(result.success, false); + expect(result.error, isA()); + }); + + test('restartService', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + _init(dummyData); + + final ServiceRequestResult result1 = await _startService(dummyData); + expect(result1.success, true); + expect(result1.error, isNull); + + final ServiceRequestResult result2 = await _restartService(); + expect(result2.success, true); + expect(result2.error, isNull); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.restartService, arguments: null), + ); + }); + + test('restartService (error: ServiceNotStartedException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + final ServiceRequestResult result = await _restartService(); + expect(result.success, false); + expect(result.error, isA()); + }); + + test('updateService', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + _init(dummyData); + + final ServiceRequestResult result1 = await _startService(dummyData); + expect(result1.success, true); + expect(result1.error, isNull); + + final ServiceRequestResult result2 = await _updateService(dummyData); + expect(result2.success, true); + expect(result2.error, isNull); + expect( + methodCallHandler.log.last, + isMethodCall( + ServiceApiMethod.updateService, + arguments: dummyData.getUpdateServiceArgs(), + ), + ); + }); + + test('updateService (error: ServiceNotStartedException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + final ServiceRequestResult result = await _updateService(dummyData); + expect(result.success, false); + expect(result.error, isA()); + }); + + test('stopService', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + FlutterForegroundTask.skipServiceResponseCheck = true; + + _init(dummyData); + + final ServiceRequestResult result1 = await _startService(dummyData); + expect(result1.success, true); + expect(result1.error, isNull); + + final ServiceRequestResult result2 = await _stopService(); + expect(result2.success, true); + expect(result2.error, isNull); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.stopService, arguments: null), + ); + }); + + test('stopService (error: ServiceNotStartedException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + final ServiceRequestResult result = await _stopService(); + expect(result.success, false); + expect(result.error, isA()); + }); + + test('stopService (error: ServiceTimeoutException)', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + _init(dummyData); + + final ServiceRequestResult result1 = await _startService(dummyData); + expect(result1.success, true); + expect(result1.error, isNull); + + // set test + methodCallHandler.timeoutTest = true; + + final ServiceRequestResult result2 = await _stopService(); + expect(result2.success, false); + expect(result2.error, isA()); + }); + + test('isRunningService', () async { + platformChannel.platform = FakePlatform(operatingSystem: platform); + + _init(dummyData); + expect(await _isRunningService, false); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.isRunningService, arguments: null), + ); + + await _startService(dummyData); + expect(await _isRunningService, true); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.isRunningService, arguments: null), + ); + + await _restartService(); + expect(await _isRunningService, true); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.isRunningService, arguments: null), + ); + + await _updateService(dummyData); + expect(await _isRunningService, true); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.isRunningService, arguments: null), + ); + + await _stopService(); + expect(await _isRunningService, false); + expect( + methodCallHandler.log.last, + isMethodCall(ServiceApiMethod.isRunningService, arguments: null), + ); + }); }); } @@ -177,6 +493,7 @@ Future _startService(ServiceDummyData dummyData) { notificationText: dummyData.notificationText, notificationIcon: dummyData.notificationIcon, notificationButtons: dummyData.notificationButtons, + callback: testCallback, ); } @@ -191,6 +508,7 @@ Future _updateService(ServiceDummyData dummyData) { notificationText: dummyData.notificationText, notificationIcon: dummyData.notificationIcon, notificationButtons: dummyData.notificationButtons, + callback: testCallback, ); } @@ -202,123 +520,90 @@ Future get _isRunningService { return FlutterForegroundTask.isRunningService; } -class MockFlutterForegroundTask - with MockPlatformInterfaceMixin - implements FlutterForegroundTaskPlatform { - // ====================== Service ====================== - - // test options - bool timeoutTest = false; - - bool _isRunningService = false; - - @override - Future startService({ - required AndroidNotificationOptions androidNotificationOptions, - required IOSNotificationOptions iosNotificationOptions, - required ForegroundTaskOptions foregroundTaskOptions, - int? serviceId, - required String notificationTitle, - required String notificationText, - NotificationIconData? notificationIcon, - List? notificationButtons, - Function? callback, - }) async { - // official doc: Once the service has been created, the service must call its startForeground() method within five seconds. - // ref: https://developer.android.com/guide/components/services#StartingAService - if (timeoutTest) { - await Future.delayed(const Duration(milliseconds: 6000)); - return; +class ServiceApiMethod { + static const String startService = 'startService'; + static const String restartService = 'restartService'; + static const String updateService = 'updateService'; + static const String stopService = 'stopService'; + static const String isRunningService = 'isRunningService'; + static const String attachedActivity = 'attachedActivity'; + + static Set getImplementation(Platform platform) { + if (platform.isAndroid) { + return { + startService, + restartService, + updateService, + stopService, + isRunningService, + attachedActivity, + }; + } else if (platform.isIOS) { + return { + startService, + restartService, + updateService, + stopService, + isRunningService, + }; } - _isRunningService = true; - } - @override - Future restartService() async { - _isRunningService = true; + return {}; } +} - @override - Future updateService({ - ForegroundTaskOptions? foregroundTaskOptions, - String? notificationTitle, - String? notificationText, - NotificationIconData? notificationIcon, - List? notificationButtons, - Function? callback, - }) async { - _isRunningService = true; - } - - @override - Future stopService() async { - // official doc: Once the service has been created, the service must call its startForeground() method within five seconds. - // ref: https://developer.android.com/guide/components/services#StartingAService - if (timeoutTest) { - await Future.delayed(const Duration(milliseconds: 6000)); - return; - } - _isRunningService = false; - } - - @override - Future get isRunningService async => _isRunningService; - - @override - Future get attachedActivity async => true; - - @override - void setTaskHandler(TaskHandler handler) => throw UnimplementedError(); - - // =================== Communication =================== - - @override - void sendDataToTask(Object data) => throw UnimplementedError(); - - // ====================== Utility ====================== - - @override - void minimizeApp() => throw UnimplementedError(); - - @override - void launchApp([String? route]) => throw UnimplementedError(); - - @override - void setOnLockScreenVisibility(bool isVisible) => throw UnimplementedError(); - - @override - Future get isAppOnForeground => throw UnimplementedError(); - - @override - void wakeUpScreen() => throw UnimplementedError(); - - @override - Future get isIgnoringBatteryOptimizations => throw UnimplementedError(); - - @override - Future openIgnoreBatteryOptimizationSettings() => - throw UnimplementedError(); +class ServiceApiMethodCallHandler { + ServiceApiMethodCallHandler(this._platformGetter); - @override - Future requestIgnoreBatteryOptimization() => throw UnimplementedError(); + final ValueGetter _platformGetter; - @override - Future get canDrawOverlays => throw UnimplementedError(); + final List log = []; - @override - Future openSystemAlertWindowSettings() => throw UnimplementedError(); + bool timeoutTest = false; - @override - Future checkNotificationPermission() => - throw UnimplementedError(); + bool _isRunningService = false; - @override - Future requestNotificationPermission() => - throw UnimplementedError(); + // unimplemented: throw UnimplementedError + void _checkImplementation(String method) { + final Platform platform = _platformGetter(); + if (!ServiceApiMethod.getImplementation(platform).contains(method)) { + throw UnimplementedError( + 'Unimplemented method on ${platform.operatingSystem}: $method'); + } + } - @override - Future get canScheduleExactAlarms => throw UnimplementedError(); + Future? onMethodCall(MethodCall methodCall) async { + final String method = methodCall.method; + _checkImplementation(method); + + log.add(methodCall); + + if (method == ServiceApiMethod.startService) { + if (!timeoutTest) { + _isRunningService = true; + } + return Future.value(); + } else if (method == ServiceApiMethod.restartService) { + if (!timeoutTest) { + _isRunningService = true; + } + return Future.value(); + } else if (method == ServiceApiMethod.updateService) { + if (!timeoutTest) { + _isRunningService = true; + } + return Future.value(); + } else if (method == ServiceApiMethod.stopService) { + if (!timeoutTest) { + _isRunningService = false; + } + return Future.value(); + } else if (method == ServiceApiMethod.isRunningService) { + return _isRunningService; + } else if (method == ServiceApiMethod.attachedActivity) { + return true; + } - @override - Future openAlarmsAndRemindersSettings() => throw UnimplementedError(); + throw UnimplementedError(); + } } diff --git a/test/task_handler_test.dart b/test/task_handler_test.dart index 82610ac6..e6663c71 100644 --- a/test/task_handler_test.dart +++ b/test/task_handler_test.dart @@ -7,25 +7,26 @@ import 'package:flutter_test/flutter_test.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - late MethodChannelFlutterForegroundTask platform; - late TestTaskHandler testTaskHandler; + late MethodChannelFlutterForegroundTask platformChannel; + late TestTaskHandler taskHandler; setUp(() { - platform = MethodChannelFlutterForegroundTask(); - FlutterForegroundTaskPlatform.instance = platform; + platformChannel = MethodChannelFlutterForegroundTask(); + FlutterForegroundTaskPlatform.instance = platformChannel; FlutterForegroundTask.resetStatic(); - testTaskHandler = TestTaskHandler(); + taskHandler = TestTaskHandler(); // method channel TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler( - platform.mMDChannel, + platformChannel.mMDChannel, (MethodCall methodCall) async { final String method = methodCall.method; if (method == 'sendData') { final dynamic data = methodCall.arguments; - platform.mBGChannel.invokeMethod(TaskEventMethod.onReceiveData, data); + platformChannel.mBGChannel + .invokeMethod(TaskEventMethod.onReceiveData, data); } return; }, @@ -34,9 +35,9 @@ void main() { // background channel TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler( - platform.mBGChannel, + platformChannel.mBGChannel, (MethodCall methodCall) async { - platform.onBackgroundChannelMethodCall(methodCall, testTaskHandler); + platformChannel.onBackgroundChannelMethodCall(methodCall, taskHandler); return; }, ); @@ -44,81 +45,81 @@ void main() { tearDown(() { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(platform.mMDChannel, null); + .setMockMethodCallHandler(platformChannel.mMDChannel, null); TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(platform.mBGChannel, null); + .setMockMethodCallHandler(platformChannel.mBGChannel, null); }); group('TaskHandler', () { test('onStart', () async { const String method = TaskEventMethod.onStart; - await platform.mBGChannel.invokeMethod(method); - expect(testTaskHandler.log.last, isTaskEvent(method)); + await platformChannel.mBGChannel.invokeMethod(method); + expect(taskHandler.log.last, isTaskEvent(method)); }); test('onRepeatEvent', () async { const String method = TaskEventMethod.onRepeatEvent; - await platform.mBGChannel.invokeMethod(method); - expect(testTaskHandler.log.last, isTaskEvent(method)); + await platformChannel.mBGChannel.invokeMethod(method); + expect(taskHandler.log.last, isTaskEvent(method)); }); test('onDestroy', () async { const String method = TaskEventMethod.onDestroy; - await platform.mBGChannel.invokeMethod(method); - expect(testTaskHandler.log.last, isTaskEvent(method)); + await platformChannel.mBGChannel.invokeMethod(method); + expect(taskHandler.log.last, isTaskEvent(method)); }); test('onReceiveData', () async { const String method = TaskEventMethod.onReceiveData; const String stringData = 'hello'; - await platform.mBGChannel.invokeMethod(method, stringData); - expect(testTaskHandler.log.last, isTaskEvent(method, stringData)); + await platformChannel.mBGChannel.invokeMethod(method, stringData); + expect(taskHandler.log.last, isTaskEvent(method, stringData)); const int intData = 1234; - await platform.mBGChannel.invokeMethod(method, intData); - expect(testTaskHandler.log.last, isTaskEvent(method, intData)); + await platformChannel.mBGChannel.invokeMethod(method, intData); + expect(taskHandler.log.last, isTaskEvent(method, intData)); const double doubleData = 1.234; - await platform.mBGChannel.invokeMethod(method, doubleData); - expect(testTaskHandler.log.last, isTaskEvent(method, doubleData)); + await platformChannel.mBGChannel.invokeMethod(method, doubleData); + expect(taskHandler.log.last, isTaskEvent(method, doubleData)); const bool boolData = false; - await platform.mBGChannel.invokeMethod(method, boolData); - expect(testTaskHandler.log.last, isTaskEvent(method, boolData)); + await platformChannel.mBGChannel.invokeMethod(method, boolData); + expect(taskHandler.log.last, isTaskEvent(method, boolData)); const List listData = [1, 2, 3]; - await platform.mBGChannel.invokeMethod(method, listData); - expect(testTaskHandler.log.last, isTaskEvent(method, listData)); + await platformChannel.mBGChannel.invokeMethod(method, listData); + expect(taskHandler.log.last, isTaskEvent(method, listData)); const Map mapData = {'message': 'hello', 'data': 1}; - await platform.mBGChannel.invokeMethod(method, mapData); - expect(testTaskHandler.log.last, isTaskEvent(method, mapData)); + await platformChannel.mBGChannel.invokeMethod(method, mapData); + expect(taskHandler.log.last, isTaskEvent(method, mapData)); }); test('onNotificationButtonPressed', () async { const String method = TaskEventMethod.onNotificationButtonPressed; const String data = 'id_hello'; - await platform.mBGChannel.invokeMethod(method, data); - expect(testTaskHandler.log.last, isTaskEvent(method, data)); + await platformChannel.mBGChannel.invokeMethod(method, data); + expect(taskHandler.log.last, isTaskEvent(method, data)); }); test('onNotificationPressed', () async { const String method = TaskEventMethod.onNotificationPressed; - await platform.mBGChannel.invokeMethod(method); - expect(testTaskHandler.log.last, isTaskEvent(method)); + await platformChannel.mBGChannel.invokeMethod(method); + expect(taskHandler.log.last, isTaskEvent(method)); }); test('onNotificationDismissed', () async { const String method = TaskEventMethod.onNotificationDismissed; - await platform.mBGChannel.invokeMethod(method); - expect(testTaskHandler.log.last, isTaskEvent(method)); + await platformChannel.mBGChannel.invokeMethod(method); + expect(taskHandler.log.last, isTaskEvent(method)); }); }); @@ -168,27 +169,27 @@ void main() { const String stringData = 'hello'; FlutterForegroundTask.sendDataToTask(stringData); - expect(testTaskHandler.log.last, isTaskEvent(method, stringData)); + expect(taskHandler.log.last, isTaskEvent(method, stringData)); const int intData = 1234; FlutterForegroundTask.sendDataToTask(intData); - expect(testTaskHandler.log.last, isTaskEvent(method, intData)); + expect(taskHandler.log.last, isTaskEvent(method, intData)); const double doubleData = 1.234; FlutterForegroundTask.sendDataToTask(doubleData); - expect(testTaskHandler.log.last, isTaskEvent(method, doubleData)); + expect(taskHandler.log.last, isTaskEvent(method, doubleData)); const bool boolData = false; FlutterForegroundTask.sendDataToTask(boolData); - expect(testTaskHandler.log.last, isTaskEvent(method, boolData)); + expect(taskHandler.log.last, isTaskEvent(method, boolData)); const List listData = [1, 2, 3]; FlutterForegroundTask.sendDataToTask(listData); - expect(testTaskHandler.log.last, isTaskEvent(method, listData)); + expect(taskHandler.log.last, isTaskEvent(method, listData)); const Map mapData = {'message': 'hello', 'data': 1}; FlutterForegroundTask.sendDataToTask(mapData); - expect(testTaskHandler.log.last, isTaskEvent(method, mapData)); + expect(taskHandler.log.last, isTaskEvent(method, mapData)); }); }); } diff --git a/test/utility_test.dart b/test/utility_test.dart index f3b106ff..34439548 100644 --- a/test/utility_test.dart +++ b/test/utility_test.dart @@ -9,30 +9,35 @@ import 'package:platform/platform.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - late MethodChannelFlutterForegroundTask platform; + late MethodChannelFlutterForegroundTask platformChannel; late UtilityMethodCallHandler methodCallHandler; setUp(() { - platform = MethodChannelFlutterForegroundTask(); - FlutterForegroundTaskPlatform.instance = platform; - methodCallHandler = UtilityMethodCallHandler(() => platform.platform); + platformChannel = MethodChannelFlutterForegroundTask(); + FlutterForegroundTaskPlatform.instance = platformChannel; + FlutterForegroundTask.resetStatic(); + + methodCallHandler = + UtilityMethodCallHandler(() => platformChannel.platform); // method channel TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler( - platform.mMDChannel, + platformChannel.mMDChannel, methodCallHandler.onMethodCall, ); }); tearDown(() { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(platform.mMDChannel, null); + .setMockMethodCallHandler(platformChannel.mMDChannel, null); }); group('Android', () { + const String platform = Platform.android; + test('minimizeApp', () { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); FlutterForegroundTask.minimizeApp(); expect( @@ -42,7 +47,7 @@ void main() { }); test('launchApp', () { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); FlutterForegroundTask.launchApp(); expect( @@ -58,7 +63,7 @@ void main() { }); test('setOnLockScreenVisibility', () { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); FlutterForegroundTask.setOnLockScreenVisibility(true); expect( @@ -80,7 +85,7 @@ void main() { }); test('isAppOnForeground', () async { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.isAppOnForeground; expect(result, false); @@ -91,7 +96,7 @@ void main() { }); test('wakeUpScreen', () { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); FlutterForegroundTask.wakeUpScreen(); expect( @@ -101,7 +106,7 @@ void main() { }); test('isIgnoringBatteryOptimizations', () async { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.isIgnoringBatteryOptimizations; @@ -116,7 +121,7 @@ void main() { }); test('openIgnoreBatteryOptimizationSettings', () async { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.openIgnoreBatteryOptimizationSettings(); @@ -131,7 +136,7 @@ void main() { }); test('requestIgnoreBatteryOptimization', () async { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.requestIgnoreBatteryOptimization(); @@ -146,7 +151,7 @@ void main() { }); test('canDrawOverlays', () async { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.canDrawOverlays; expect(result, false); @@ -157,7 +162,7 @@ void main() { }); test('openSystemAlertWindowSettings', () async { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.openSystemAlertWindowSettings(); @@ -172,7 +177,7 @@ void main() { }); test('checkNotificationPermission', () async { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); final NotificationPermission permission = await FlutterForegroundTask.checkNotificationPermission(); @@ -187,7 +192,7 @@ void main() { }); test('requestNotificationPermission', () async { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); final NotificationPermission permission = await FlutterForegroundTask.requestNotificationPermission(); @@ -202,7 +207,7 @@ void main() { }); test('canScheduleExactAlarms', () async { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.canScheduleExactAlarms; expect(result, false); @@ -213,7 +218,7 @@ void main() { }); test('openAlarmsAndRemindersSettings', () async { - platform.platform = FakePlatform(operatingSystem: Platform.android); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.openAlarmsAndRemindersSettings(); @@ -229,8 +234,10 @@ void main() { }); group('iOS', () { + const String platform = Platform.iOS; + test('minimizeApp', () { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); FlutterForegroundTask.minimizeApp(); expect( @@ -240,7 +247,7 @@ void main() { }); test('launchApp', () { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); FlutterForegroundTask.launchApp(); expect(methodCallHandler.log, isEmpty); @@ -250,7 +257,7 @@ void main() { }); test('setOnLockScreenVisibility', () { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); FlutterForegroundTask.setOnLockScreenVisibility(true); expect(methodCallHandler.log, isEmpty); @@ -260,7 +267,7 @@ void main() { }); test('isAppOnForeground', () async { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.isAppOnForeground; expect(result, false); @@ -271,14 +278,14 @@ void main() { }); test('wakeUpScreen', () { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); FlutterForegroundTask.wakeUpScreen(); expect(methodCallHandler.log, isEmpty); }); test('isIgnoringBatteryOptimizations', () async { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.isIgnoringBatteryOptimizations; @@ -287,7 +294,7 @@ void main() { }); test('openIgnoreBatteryOptimizationSettings', () async { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.openIgnoreBatteryOptimizationSettings(); @@ -296,7 +303,7 @@ void main() { }); test('requestIgnoreBatteryOptimization', () async { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.requestIgnoreBatteryOptimization(); @@ -305,7 +312,7 @@ void main() { }); test('canDrawOverlays', () async { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.canDrawOverlays; expect(result, true); @@ -313,7 +320,7 @@ void main() { }); test('openSystemAlertWindowSettings', () async { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.openSystemAlertWindowSettings(); @@ -322,7 +329,7 @@ void main() { }); test('checkNotificationPermission', () async { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); final NotificationPermission permission = await FlutterForegroundTask.checkNotificationPermission(); @@ -337,7 +344,7 @@ void main() { }); test('requestNotificationPermission', () async { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); final NotificationPermission permission = await FlutterForegroundTask.requestNotificationPermission(); @@ -352,7 +359,7 @@ void main() { }); test('canScheduleExactAlarms', () async { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.canScheduleExactAlarms; expect(result, true); @@ -360,7 +367,7 @@ void main() { }); test('openAlarmsAndRemindersSettings', () async { - platform.platform = FakePlatform(operatingSystem: Platform.iOS); + platformChannel.platform = FakePlatform(operatingSystem: platform); final bool result = await FlutterForegroundTask.openAlarmsAndRemindersSettings();