diff --git a/packages/android_alarm_manager/CHANGELOG.md b/packages/android_alarm_manager/CHANGELOG.md index 1506fd304cf7..1494506fcb73 100644 --- a/packages/android_alarm_manager/CHANGELOG.md +++ b/packages/android_alarm_manager/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.4+3 + +* Add unit tests and DartDocs. + ## 0.4.4+2 * Remove AndroidX warning. diff --git a/packages/android_alarm_manager/analysis_options.yaml b/packages/android_alarm_manager/analysis_options.yaml deleted file mode 100644 index 8e4af76f0a30..000000000000 --- a/packages/android_alarm_manager/analysis_options.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# This is a temporary file to allow us to land a new set of linter rules in a -# series of manageable patches instead of one gigantic PR. It disables some of -# the new lints that are already failing on this plugin, for this plugin. It -# should be deleted and the failing lints addressed as soon as possible. - -include: ../../analysis_options.yaml - -analyzer: - errors: - public_member_api_docs: ignore diff --git a/packages/android_alarm_manager/example/lib/main.dart b/packages/android_alarm_manager/example/lib/main.dart index e68735a75085..5e20364312bb 100644 --- a/packages/android_alarm_manager/example/lib/main.dart +++ b/packages/android_alarm_manager/example/lib/main.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: public_member_api_docs + import 'dart:async'; import 'package:android_alarm_manager/android_alarm_manager.dart'; diff --git a/packages/android_alarm_manager/lib/android_alarm_manager.dart b/packages/android_alarm_manager/lib/android_alarm_manager.dart index 76994b9edac7..f31b4bc87525 100644 --- a/packages/android_alarm_manager/lib/android_alarm_manager.dart +++ b/packages/android_alarm_manager/lib/android_alarm_manager.dart @@ -54,14 +54,34 @@ void _alarmManagerCallbackDispatcher() { _channel.invokeMethod('AlarmService.initialized'); } +// A lambda that returns the current instant in the form of a [DateTime]. +typedef DateTime _Now(); +// A lambda that gets the handle for the given [callback]. +typedef CallbackHandle _GetCallbackHandle(Function callback); + /// A Flutter plugin for registering Dart callbacks with the Android /// AlarmManager service. /// /// See the example/ directory in this package for sample usage. class AndroidAlarmManager { static const String _channelName = 'plugins.flutter.io/android_alarm_manager'; - static const MethodChannel _channel = - MethodChannel(_channelName, JSONMethodCodec()); + static MethodChannel _channel = + const MethodChannel(_channelName, JSONMethodCodec()); + // Function used to get the current time. It's [DateTime.now] by default. + static _Now _now = () => DateTime.now(); + // Callback used to get the handle for a callback. It's + // [PluginUtilities.getCallbackHandle] by default. + static _GetCallbackHandle _getCallbackHandle = + (Function callback) => PluginUtilities.getCallbackHandle(callback); + + /// This is exposed for the unit tests. It should not be accessed by users of + /// the plugin. + @visibleForTesting + static void setTestOverides( + {_Now now, _GetCallbackHandle getCallbackHandle}) { + _now = (now ?? _now); + _getCallbackHandle = (getCallbackHandle ?? _getCallbackHandle); + } /// Starts the [AndroidAlarmManager] service. This must be called before /// setting any alarms. @@ -70,7 +90,7 @@ class AndroidAlarmManager { /// failure. static Future initialize() async { final CallbackHandle handle = - PluginUtilities.getCallbackHandle(_alarmManagerCallbackDispatcher); + _getCallbackHandle(_alarmManagerCallbackDispatcher); if (handle == null) { return false; } @@ -127,7 +147,7 @@ class AndroidAlarmManager { bool rescheduleOnReboot = false, }) => oneShotAt( - DateTime.now().add(delay), + _now().add(delay), id, callback, alarmClock: alarmClock, @@ -188,7 +208,7 @@ class AndroidAlarmManager { assert(callback is Function() || callback is Function(int)); assert(id.bitLength < 32); final int startMillis = time.millisecondsSinceEpoch; - final CallbackHandle handle = PluginUtilities.getCallbackHandle(callback); + final CallbackHandle handle = _getCallbackHandle(callback); if (handle == null) { return false; } @@ -251,11 +271,11 @@ class AndroidAlarmManager { // ignore: inference_failure_on_function_return_type assert(callback is Function() || callback is Function(int)); assert(id.bitLength < 32); - final int now = DateTime.now().millisecondsSinceEpoch; + final int now = _now().millisecondsSinceEpoch; final int period = duration.inMilliseconds; final int first = startAt != null ? startAt.millisecondsSinceEpoch : now + period; - final CallbackHandle handle = PluginUtilities.getCallbackHandle(callback); + final CallbackHandle handle = _getCallbackHandle(callback); if (handle == null) { return false; } diff --git a/packages/android_alarm_manager/pubspec.yaml b/packages/android_alarm_manager/pubspec.yaml index 6fe4ed944d55..29a9961b448d 100644 --- a/packages/android_alarm_manager/pubspec.yaml +++ b/packages/android_alarm_manager/pubspec.yaml @@ -1,7 +1,7 @@ name: android_alarm_manager description: Flutter plugin for accessing the Android AlarmManager service, and running Dart code in the background when alarms fire. -version: 0.4.4+2 +version: 0.4.4+3 author: Flutter Team homepage: https://github.com/flutter/plugins/tree/master/packages/android_alarm_manager @@ -9,6 +9,10 @@ dependencies: flutter: sdk: flutter +dev_dependencies: + flutter_test: + sdk: flutter + flutter: plugin: androidPackage: io.flutter.plugins.androidalarmmanager diff --git a/packages/android_alarm_manager/test/android_alarm_manager_test.dart b/packages/android_alarm_manager/test/android_alarm_manager_test.dart new file mode 100644 index 000000000000..1f9d2856838e --- /dev/null +++ b/packages/android_alarm_manager/test/android_alarm_manager_test.dart @@ -0,0 +1,201 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ui'; + +import 'package:android_alarm_manager/android_alarm_manager.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + String invalidCallback(String foo) => foo; + void validCallback(int id) => null; + + const MethodChannel testChannel = MethodChannel( + 'plugins.flutter.io/android_alarm_manager', JSONMethodCodec()); + TestWidgetsFlutterBinding.ensureInitialized(); + + setUpAll(() { + testChannel.setMockMethodCallHandler((MethodCall call) => null); + }); + + test('${AndroidAlarmManager.initialize}', () async { + testChannel.setMockMethodCallHandler((MethodCall call) async { + assert(call.method == 'AlarmService.start'); + return true; + }); + + final bool initialized = await AndroidAlarmManager.initialize(); + + expect(initialized, isTrue); + }); + + group('${AndroidAlarmManager.oneShotAt}', () { + test('validates input', () async { + final DateTime validTime = DateTime.utc(1993); + final int validId = 1; + + // Callback should take a single int param. + await expectLater( + () => AndroidAlarmManager.oneShotAt( + validTime, validId, invalidCallback), + throwsAssertionError); + + // ID should be less than 32 bits. + await expectLater( + () => AndroidAlarmManager.oneShotAt( + validTime, 2147483648, validCallback), + throwsAssertionError); + }); + + test('sends arguments to the platform', () async { + final DateTime alarm = DateTime(1993); + const int rawHandle = 4; + AndroidAlarmManager.setTestOverides( + getCallbackHandle: (Function _) => + CallbackHandle.fromRawHandle(rawHandle)); + + final int id = 1; + final bool alarmClock = true; + final bool allowWhileIdle = true; + final bool exact = true; + final bool wakeup = true; + final bool rescheduleOnReboot = true; + + testChannel.setMockMethodCallHandler((MethodCall call) async { + expect(call.method, 'Alarm.oneShotAt'); + expect(call.arguments[0], id); + expect(call.arguments[1], alarmClock); + expect(call.arguments[2], allowWhileIdle); + expect(call.arguments[3], exact); + expect(call.arguments[4], wakeup); + expect(call.arguments[5], alarm.millisecondsSinceEpoch); + expect(call.arguments[6], rescheduleOnReboot); + expect(call.arguments[7], rawHandle); + return true; + }); + + final bool result = await AndroidAlarmManager.oneShotAt( + alarm, id, validCallback, + alarmClock: alarmClock, + allowWhileIdle: allowWhileIdle, + exact: exact, + wakeup: wakeup, + rescheduleOnReboot: rescheduleOnReboot); + + expect(result, isTrue); + }); + }); + + test('${AndroidAlarmManager.oneShot} calls through to oneShotAt', () async { + final DateTime now = DateTime(1993); + const int rawHandle = 4; + AndroidAlarmManager.setTestOverides( + now: () => now, + getCallbackHandle: (Function _) => + CallbackHandle.fromRawHandle(rawHandle)); + + const Duration alarm = Duration(seconds: 1); + final int id = 1; + final bool alarmClock = true; + final bool allowWhileIdle = true; + final bool exact = true; + final bool wakeup = true; + final bool rescheduleOnReboot = true; + + testChannel.setMockMethodCallHandler((MethodCall call) async { + expect(call.method, 'Alarm.oneShotAt'); + expect(call.arguments[0], id); + expect(call.arguments[1], alarmClock); + expect(call.arguments[2], allowWhileIdle); + expect(call.arguments[3], exact); + expect(call.arguments[4], wakeup); + expect( + call.arguments[5], now.millisecondsSinceEpoch + alarm.inMilliseconds); + expect(call.arguments[6], rescheduleOnReboot); + expect(call.arguments[7], rawHandle); + return true; + }); + + final bool result = await AndroidAlarmManager.oneShot( + alarm, id, validCallback, + alarmClock: alarmClock, + allowWhileIdle: allowWhileIdle, + exact: exact, + wakeup: wakeup, + rescheduleOnReboot: rescheduleOnReboot); + + expect(result, isTrue); + }); + + group('${AndroidAlarmManager.periodic}', () { + test('validates input', () async { + const Duration validDuration = Duration(seconds: 0); + final int validId = 1; + + // Callback should take a single int param. + await expectLater( + () => AndroidAlarmManager.periodic( + validDuration, validId, invalidCallback), + throwsAssertionError); + + // ID should be less than 32 bits. + await expectLater( + () => AndroidAlarmManager.periodic( + validDuration, 2147483648, validCallback), + throwsAssertionError); + }); + + test('sends arguments through to the platform', () async { + final DateTime now = DateTime(1993); + const int rawHandle = 4; + AndroidAlarmManager.setTestOverides( + now: () => now, + getCallbackHandle: (Function _) => + CallbackHandle.fromRawHandle(rawHandle)); + + final int id = 1; + final bool exact = true; + final bool wakeup = true; + final bool rescheduleOnReboot = true; + const Duration period = Duration(seconds: 1); + + testChannel.setMockMethodCallHandler((MethodCall call) async { + expect(call.method, 'Alarm.periodic'); + expect(call.arguments[0], id); + expect(call.arguments[1], exact); + expect(call.arguments[2], wakeup); + expect(call.arguments[3], + (now.millisecondsSinceEpoch + period.inMilliseconds)); + expect(call.arguments[4], period.inMilliseconds); + expect(call.arguments[5], rescheduleOnReboot); + expect(call.arguments[6], rawHandle); + return true; + }); + + final bool result = await AndroidAlarmManager.periodic( + period, + id, + (int id) => null, + exact: exact, + wakeup: wakeup, + rescheduleOnReboot: rescheduleOnReboot, + ); + + expect(result, isTrue); + }); + }); + + test('${AndroidAlarmManager.cancel}', () async { + final int id = 1; + testChannel.setMockMethodCallHandler((MethodCall call) async { + assert(call.method == 'Alarm.cancel' && call.arguments[0] == id); + return true; + }); + + final bool canceled = await AndroidAlarmManager.cancel(id); + + expect(canceled, isTrue); + }); +}