[android_alarm_manager] Testing and documentation (#2283)
* [android_alarm_manager] Testing and documentation
Unit tests the plugin, and adds a lint for missing DartDocs.
* Review feedback
- Wrap InstanceWrappers into a single `setTestOverride` method.
- Replace potentially garbage collected lambdas with top level
functions.
* Review feedback
diff --git a/packages/android_alarm_manager/CHANGELOG.md b/packages/android_alarm_manager/CHANGELOG.md
index 1506fd3..1494506 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 8e4af76..0000000
--- 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 e68735a..5e20364 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 76994b9..f31b4bc 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 @@
_channel.invokeMethod<void>('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 @@
/// failure.
static Future<bool> initialize() async {
final CallbackHandle handle =
- PluginUtilities.getCallbackHandle(_alarmManagerCallbackDispatcher);
+ _getCallbackHandle(_alarmManagerCallbackDispatcher);
if (handle == null) {
return false;
}
@@ -127,7 +147,7 @@
bool rescheduleOnReboot = false,
}) =>
oneShotAt(
- DateTime.now().add(delay),
+ _now().add(delay),
id,
callback,
alarmClock: alarmClock,
@@ -188,7 +208,7 @@
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 @@
// 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 6fe4ed9..29a9961 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 <flutter-dev@googlegroups.com>
homepage: https://github.com/flutter/plugins/tree/master/packages/android_alarm_manager
@@ -9,6 +9,10 @@
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 0000000..1f9d285
--- /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);
+ });
+}