blob: 52da47c4e9919a5b0cdea07b62da48119c8e0cff [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.calendar.alerts;
import static android.app.Notification.PRIORITY_DEFAULT;
import static android.app.Notification.PRIORITY_HIGH;
import static android.app.Notification.PRIORITY_MIN;
import android.app.AlarmManager;
import android.content.SharedPreferences;
import android.database.MatrixCursor;
import android.provider.CalendarContract.Attendees;
import android.provider.CalendarContract.CalendarAlerts;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.Smoke;
import android.text.format.DateUtils;
import android.text.format.Time;
import com.android.calendar.GeneralPreferences;
import com.android.calendar.alerts.AlertService.NotificationInfo;
import com.android.calendar.alerts.AlertService.NotificationWrapper;
import junit.framework.Assert;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
public class AlertServiceTest extends AndroidTestCase {
class MockSharedPreferences implements SharedPreferences {
private Boolean mVibrate;
private String mRingtone;
private Boolean mPopup;
// Strict mode will fail if a preference key is queried more than once.
private boolean mStrict = false;
MockSharedPreferences() {
this(false);
}
MockSharedPreferences(boolean strict) {
super();
init();
this.mStrict = strict;
}
void init() {
mVibrate = true;
mRingtone = "/some/cool/ringtone";
mPopup = true;
}
@Override
public boolean contains(String key) {
if (GeneralPreferences.KEY_ALERTS_VIBRATE.equals(key)) {
return true;
}
return false;
}
@Override
public boolean getBoolean(String key, boolean defValue) {
if (GeneralPreferences.KEY_ALERTS_VIBRATE.equals(key)) {
if (mVibrate == null) {
Assert.fail(GeneralPreferences.KEY_ALERTS_VIBRATE
+ " fetched more than once.");
}
boolean val = mVibrate;
if (mStrict) {
mVibrate = null;
}
return val;
}
if (GeneralPreferences.KEY_ALERTS_POPUP.equals(key)) {
if (mPopup == null) {
Assert.fail(GeneralPreferences.KEY_ALERTS_POPUP + " fetched more than once.");
}
boolean val = mPopup;
if (mStrict) {
mPopup = null;
}
return val;
}
throw new IllegalArgumentException();
}
@Override
public String getString(String key, String defValue) {
if (GeneralPreferences.KEY_ALERTS_RINGTONE.equals(key)) {
if (mRingtone == null) {
Assert.fail(GeneralPreferences.KEY_ALERTS_RINGTONE
+ " fetched more than once.");
}
String val = mRingtone;
if (mStrict) {
mRingtone = null;
}
return val;
}
throw new IllegalArgumentException();
}
@Override
public Map<String, ?> getAll() {
throw new IllegalArgumentException();
}
@Override
public Set<String> getStringSet(String key, Set<String> defValues) {
throw new IllegalArgumentException();
}
@Override
public int getInt(String key, int defValue) {
throw new IllegalArgumentException();
}
@Override
public long getLong(String key, long defValue) {
throw new IllegalArgumentException();
}
@Override
public float getFloat(String key, float defValue) {
throw new IllegalArgumentException();
}
@Override
public Editor edit() {
throw new IllegalArgumentException();
}
@Override
public void registerOnSharedPreferenceChangeListener(
OnSharedPreferenceChangeListener listener) {
throw new IllegalArgumentException();
}
@Override
public void unregisterOnSharedPreferenceChangeListener(
OnSharedPreferenceChangeListener listener) {
throw new IllegalArgumentException();
}
}
// Created these constants so the test cases are shorter
public static final int SCHEDULED = CalendarAlerts.STATE_SCHEDULED;
public static final int FIRED = CalendarAlerts.STATE_FIRED;
public static final int DISMISSED = CalendarAlerts.STATE_DISMISSED;
public static final int ACCEPTED = Attendees.ATTENDEE_STATUS_ACCEPTED;
public static final int DECLINED = Attendees.ATTENDEE_STATUS_DECLINED;
public static final int INVITED = Attendees.ATTENDEE_STATUS_INVITED;
public static final int TENTATIVE = Attendees.ATTENDEE_STATUS_TENTATIVE;
class NotificationInstance {
int mAlertId;
int[] mAlertIdsInDigest;
int mPriority;
public NotificationInstance(int alertId, int priority) {
mAlertId = alertId;
mPriority = priority;
}
public NotificationInstance(int[] alertIdsInDigest, int priority) {
mAlertIdsInDigest = alertIdsInDigest;
mPriority = priority;
}
}
class Alert {
long mEventId;
int mAlertStatus;
int mResponseStatus;
int mAllDay;
long mBegin;
long mEnd;
int mMinute;
long mAlarmTime;
public Alert(long eventId, int alertStatus, int responseStatus, int allDay, long begin,
long end, int minute, long alarmTime) {
mEventId = eventId;
mAlertStatus = alertStatus;
mResponseStatus = responseStatus;
mAllDay = allDay;
mBegin = begin;
mEnd = end;
mMinute = minute;
mAlarmTime = alarmTime;
}
}
class AlertsTable {
ArrayList<Alert> mAlerts = new ArrayList<Alert>();
int addAlertRow(long eventId, int alertStatus, int responseStatus, int allDay, long begin,
long end, long alarmTime) {
Alert a = new Alert(eventId, alertStatus, responseStatus, allDay, begin, end,
5 /* minute */, alarmTime);
int id = mAlerts.size();
mAlerts.add(a);
return id;
}
public MatrixCursor getAlertCursor() {
MatrixCursor alertCursor = new MatrixCursor(AlertService.ALERT_PROJECTION);
int i = 0;
for (Alert a : mAlerts) {
Object[] ca = {
i++,
a.mEventId,
a.mAlertStatus,
"Title" + a.mEventId + " " + a.mMinute,
"Loc" + a.mEventId,
a.mResponseStatus,
a.mAllDay,
a.mAlarmTime > 0 ? a.mAlarmTime : a.mBegin - a.mMinute * 60 * 1000,
a.mMinute,
a.mBegin,
a.mEnd,
"Desc: " + a.mAlarmTime
};
alertCursor.addRow(ca);
}
return alertCursor;
}
}
class NotificationTestManager extends NotificationMgr {
// Expected notifications
NotificationInstance[] mExpectedNotifications;
NotificationWrapper[] mActualNotifications;
boolean[] mCancelled;
// CalendarAlerts table
private ArrayList<Alert> mAlerts;
public NotificationTestManager(ArrayList<Alert> alerts, int maxNotifications) {
assertEquals(0, AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID);
mAlerts = alerts;
mExpectedNotifications = new NotificationInstance[maxNotifications + 1];
mActualNotifications = new NotificationWrapper[mExpectedNotifications.length];
mCancelled = new boolean[mExpectedNotifications.length];
}
public void expectTestNotification(int notificationId, int alertId, int highPriority) {
mExpectedNotifications[notificationId] = new NotificationInstance(alertId,
highPriority);
}
public void expectTestNotification(int notificationId, int[] alertIds, int priority) {
mExpectedNotifications[notificationId] = new NotificationInstance(alertIds, priority);
}
private <T> boolean nullContents(T[] array) {
for (T item : array) {
if (item != null) {
return false;
}
}
return true;
}
public void validateNotificationsAndReset() {
if (nullContents(mExpectedNotifications)) {
return;
}
String debugStr = printActualNotifications();
for (int id = 0; id < mActualNotifications.length; id++) {
NotificationInstance expected = mExpectedNotifications[id];
NotificationWrapper actual = mActualNotifications[id];
if (expected == null) {
assertNull("Received unexpected notificationId " + id + debugStr, actual);
assertTrue("NotificationId " + id + " should have been cancelled." + debugStr,
mCancelled[id]);
} else {
assertNotNull("Expected notificationId " + id + " but it was not posted."
+ debugStr, actual);
assertFalse("NotificationId " + id + " should not have been cancelled."
+ debugStr, mCancelled[id]);
assertEquals("Priority not as expected for notification " + id + debugStr,
expected.mPriority, actual.mNotification.priority);
if (expected.mAlertIdsInDigest == null) {
Alert a = mAlerts.get(expected.mAlertId);
assertEquals("Event ID not expected for notification " + id + debugStr,
a.mEventId, actual.mEventId);
assertEquals("Begin time not expected for notification " + id + debugStr,
a.mBegin, actual.mBegin);
assertEquals("End time not expected for notification " + id + debugStr,
a.mEnd, actual.mEnd);
} else {
// Notification should be a digest.
assertNotNull("Posted notification not a digest as expected." + debugStr,
actual.mNw);
assertEquals("Number of notifications in digest not as expected."
+ debugStr, expected.mAlertIdsInDigest.length, actual.mNw.size());
for (int i = 0; i < actual.mNw.size(); i++) {
Alert a = mAlerts.get(expected.mAlertIdsInDigest[i]);
assertEquals("Digest item " + i + ": Event ID not as expected"
+ debugStr, a.mEventId, actual.mNw.get(i).mEventId);
assertEquals("Digest item " + i + ": Begin time in digest not expected"
+ debugStr, a.mBegin, actual.mNw.get(i).mBegin);
assertEquals("Digest item " + i + ": End time in digest not expected"
+ debugStr, a.mEnd, actual.mNw.get(i).mEnd);
}
}
}
}
Arrays.fill(mCancelled, false);
Arrays.fill(mExpectedNotifications, null);
Arrays.fill(mActualNotifications, null);
}
private String printActualNotifications() {
StringBuilder s = new StringBuilder();
s.append("\n\nNotifications actually posted:\n");
for (int i = mActualNotifications.length - 1; i >= 0; i--) {
NotificationWrapper actual = mActualNotifications[i];
if (actual == null) {
continue;
}
s.append("Notification " + i + " -- ");
s.append("priority:" + actual.mNotification.priority);
if (actual.mNw == null) {
s.append(", eventId:" + actual.mEventId);
} else {
s.append(", eventIds:{");
for (int digestIndex = 0; digestIndex < actual.mNw.size(); digestIndex++) {
s.append(actual.mNw.get(digestIndex).mEventId + ",");
}
s.append("}");
}
s.append("\n");
}
return s.toString();
}
///////////////////////////////
// NotificationMgr methods
@Override
public void cancel(int id) {
assertTrue("id out of bound: " + id, 0 <= id);
assertTrue("id out of bound: " + id, id < mCancelled.length);
assertNull("id already used", mActualNotifications[id]);
assertFalse("id already used", mCancelled[id]);
mCancelled[id] = true;
assertNull("Unexpected cancel for id " + id, mExpectedNotifications[id]);
}
@Override
public void notify(int id, NotificationWrapper nw) {
assertTrue("id out of bound: " + id, 0 <= id);
assertTrue("id out of bound: " + id, id < mExpectedNotifications.length);
assertNull("id already used: " + id, mActualNotifications[id]);
mActualNotifications[id] = nw;
}
}
// TODO
// Catch updates of new state, notify time, and received time
// Test ringer, vibrate,
// Test intents, action email
@Smoke
@SmallTest
public void testGenerateAlerts_none() {
MockSharedPreferences prefs = new MockSharedPreferences();
AlertsTable at = new AlertsTable();
NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
AlertService.MAX_NOTIFICATIONS);
// Test no alert
long currentTime = 1000000;
AlertService.generateAlerts(mContext, ntm, new MockAlarmManager(mContext), prefs,
at.getAlertCursor(), currentTime, AlertService.MAX_NOTIFICATIONS);
ntm.validateNotificationsAndReset();
}
@Smoke
@SmallTest
public void testGenerateAlerts_single() {
MockSharedPreferences prefs = new MockSharedPreferences();
MockAlarmManager alarmMgr = new MockAlarmManager(mContext);
AlertsTable at = new AlertsTable();
NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
AlertService.MAX_NOTIFICATIONS);
int id = at.addAlertRow(100, SCHEDULED, ACCEPTED, 0 /* all day */, 1300000, 2300000, 0);
// Test one up coming alert
long currentTime = 1000000;
ntm.expectTestNotification(1, id, PRIORITY_HIGH);
AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(), currentTime,
AlertService.MAX_NOTIFICATIONS);
ntm.validateNotificationsAndReset(); // This wipes out notification
// tests added so far
// Test half way into an event
currentTime = 2300000;
ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id, PRIORITY_MIN);
AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(), currentTime,
AlertService.MAX_NOTIFICATIONS);
ntm.validateNotificationsAndReset();
// Test event ended
currentTime = 4300000;
ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id, PRIORITY_MIN);
AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(), currentTime,
AlertService.MAX_NOTIFICATIONS);
ntm.validateNotificationsAndReset();
}
@SmallTest
public void testGenerateAlerts_multiple() {
int maxNotifications = 10;
MockSharedPreferences prefs = new MockSharedPreferences();
MockAlarmManager alarmMgr = new MockAlarmManager(mContext);
AlertsTable at = new AlertsTable();
NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications);
// Current time - 5:00
long currentTime = createTimeInMillis(5, 0);
// Set up future alerts. The real query implementation sorts by descending start
// time so simulate that here with our order of adds to AlertsTable.
int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
createTimeInMillis(10, 0), 0);
int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0),
createTimeInMillis(9, 0), 0);
int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0),
createTimeInMillis(8, 0), 0);
// Set up concurrent alerts (that started recently).
int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0),
createTimeInMillis(5, 40), 0);
int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55),
createTimeInMillis(7, 30), 0);
int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50),
createTimeInMillis(4, 50), 0);
// Set up past alerts.
int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0),
createTimeInMillis(4, 0), 0);
int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0),
createTimeInMillis(3, 0), 0);
int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0),
createTimeInMillis(2, 0), 0);
// Check posted notifications. The order listed here is the order simulates the
// order in the real notification bar (last one posted appears on top), so these
// should be lowest start time on top.
ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent
ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent
ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent
ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future
ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future
ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
new int[] {id3, id2, id1}, PRIORITY_MIN);
AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
currentTime, maxNotifications);
ntm.validateNotificationsAndReset();
// Increase time by 15 minutes to check that some concurrent events dropped
// to the low priority bucket.
currentTime = createTimeInMillis(5, 15);
ntm.expectTestNotification(4, id5, PRIORITY_HIGH); // concurrent
ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future
ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future
ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
new int[] {id6, id4, id3, id2, id1}, PRIORITY_MIN);
AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
currentTime, maxNotifications);
ntm.validateNotificationsAndReset();
// Increase time so some of the previously future ones change state.
currentTime = createTimeInMillis(8, 15);
ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
new int[] {id8, id7, id6, id5, id4, id3, id2, id1}, PRIORITY_MIN);
AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
currentTime, maxNotifications);
ntm.validateNotificationsAndReset();
}
@SmallTest
public void testGenerateAlerts_maxAlerts() {
MockSharedPreferences prefs = new MockSharedPreferences();
MockAlarmManager alarmMgr = new MockAlarmManager(mContext);
AlertsTable at = new AlertsTable();
// Current time - 5:00
long currentTime = createTimeInMillis(5, 0);
// Set up future alerts. The real query implementation sorts by descending start
// time so simulate that here with our order of adds to AlertsTable.
int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
createTimeInMillis(10, 0), 0);
int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0),
createTimeInMillis(9, 0), 0);
int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0),
createTimeInMillis(8, 0), 0);
// Set up concurrent alerts (that started recently).
int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0),
createTimeInMillis(5, 40), 0);
int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55),
createTimeInMillis(7, 30), 0);
int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50),
createTimeInMillis(4, 50), 0);
// Set up past alerts.
int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0),
createTimeInMillis(4, 0), 0);
int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0),
createTimeInMillis(3, 0), 0);
int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0),
createTimeInMillis(2, 0), 0);
// Test when # alerts = max.
int maxNotifications = 6;
NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications);
ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent
ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent
ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent
ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future
ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future
ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
new int[] {id3, id2, id1}, PRIORITY_MIN);
AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
currentTime, maxNotifications);
ntm.validateNotificationsAndReset();
// Test when # alerts > max.
maxNotifications = 4;
ntm = new NotificationTestManager(at.mAlerts, maxNotifications);
ntm.expectTestNotification(4, id4, PRIORITY_HIGH); // concurrent
ntm.expectTestNotification(3, id5, PRIORITY_HIGH); // concurrent
ntm.expectTestNotification(2, id6, PRIORITY_HIGH); // concurrent
ntm.expectTestNotification(1, id7, PRIORITY_HIGH); // future
ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
new int[] {id9, id8, id3, id2, id1}, PRIORITY_MIN);
AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
currentTime, maxNotifications);
ntm.validateNotificationsAndReset();
}
/**
* Test that the SharedPreferences are only fetched once for each setting.
*/
@SmallTest
public void testGenerateAlerts_sharedPreferences() {
MockSharedPreferences prefs = new MockSharedPreferences(true /* strict mode */);
AlertsTable at = new AlertsTable();
NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
AlertService.MAX_NOTIFICATIONS);
// Current time - 5:00
long currentTime = createTimeInMillis(5, 0);
// Set up future alerts. The real query implementation sorts by descending start
// time so simulate that here with our order of adds to AlertsTable.
at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
createTimeInMillis(10, 0), 0);
at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0),
createTimeInMillis(9, 0), 0);
at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0),
createTimeInMillis(8, 0), 0);
// If this does not result in a failure (MockSharedPreferences fails for duplicate
// queries), then test passes.
AlertService.generateAlerts(mContext, ntm, new MockAlarmManager(mContext), prefs,
at.getAlertCursor(), currentTime, AlertService.MAX_NOTIFICATIONS);
}
public void testGenerateAlerts_refreshTime() {
AlertsTable at = new AlertsTable();
MockSharedPreferences prefs = new MockSharedPreferences();
MockAlarmManager alarmMgr = new MockAlarmManager(mContext);
NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
AlertService.MAX_NOTIFICATIONS);
// Since AlertService.processQuery uses DateUtils.isToday instead of checking against
// the passed in currentTime (not worth allocating the extra Time objects to do so), use
// today's date for this test.
Time now = new Time();
now.setToNow();
int day = now.monthDay;
int month = now.month;
int year = now.year;
Time yesterday = new Time();
yesterday.set(System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS);
Time tomorrow = new Time();
tomorrow.set(System.currentTimeMillis() + DateUtils.DAY_IN_MILLIS);
long allDayStart = Utils.createTimeInMillis(0, 0, 0, day, month, year, Time.TIMEZONE_UTC);
/* today 10am - 10:30am */
int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0,
Utils.createTimeInMillis(0, 0, 10, day, month, year, Time.getCurrentTimezone()),
Utils.createTimeInMillis(0, 30, 10, day, month, year, Time.getCurrentTimezone()),
0);
/* today 6am - 6am (0 duration event) */
int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0,
Utils.createTimeInMillis(0, 0, 6, day, month, year, Time.getCurrentTimezone()),
Utils.createTimeInMillis(0, 0, 6, day, month, year, Time.getCurrentTimezone()), 0);
/* today allDay */
int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 1, allDayStart,
allDayStart + DateUtils.HOUR_IN_MILLIS * 24, 0);
/* yesterday 11pm - today 7am (multiday event) */
int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0,
Utils.createTimeInMillis(0, 0, 23, yesterday.monthDay, yesterday.month,
yesterday.year, Time.getCurrentTimezone()),
Utils.createTimeInMillis(0, 0, 7, day, month, year, Time.getCurrentTimezone()), 0);
// Test at midnight - next refresh should be 15 min later (15 min into the all
// day event).
long currentTime = Utils.createTimeInMillis(0, 0, 0, day, month, year,
Time.getCurrentTimezone());
alarmMgr.expectAlarmTime(AlarmManager.RTC, currentTime + 15 * DateUtils.MINUTE_IN_MILLIS);
ntm.expectTestNotification(4, id1, PRIORITY_HIGH);
ntm.expectTestNotification(3, id2, PRIORITY_HIGH);
ntm.expectTestNotification(2, id3, PRIORITY_HIGH);
ntm.expectTestNotification(1, id4, PRIORITY_HIGH);
AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
currentTime, AlertService.MAX_NOTIFICATIONS);
ntm.validateNotificationsAndReset();
// Test at 12:30am - next refresh should be 30 min later (1/4 into event 'id1').
currentTime = Utils.createTimeInMillis(0, 30, 0, day, month, year,
Time.getCurrentTimezone());
alarmMgr.expectAlarmTime(AlarmManager.RTC, currentTime + 30 * DateUtils.MINUTE_IN_MILLIS);
ntm.expectTestNotification(3, id1, PRIORITY_HIGH);
ntm.expectTestNotification(2, id3, PRIORITY_HIGH);
ntm.expectTestNotification(1, id4, PRIORITY_HIGH);
ntm.expectTestNotification(4, id2, PRIORITY_DEFAULT);
AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
currentTime, AlertService.MAX_NOTIFICATIONS);
ntm.validateNotificationsAndReset();
// Test at 5:55am - next refresh should be 20 min later (15 min after 'id3').
currentTime = Utils.createTimeInMillis(0, 55, 5, day, month, year,
Time.getCurrentTimezone());
alarmMgr.expectAlarmTime(AlarmManager.RTC, currentTime + 20 * DateUtils.MINUTE_IN_MILLIS);
ntm.expectTestNotification(2, id3, PRIORITY_HIGH);
ntm.expectTestNotification(1, id4, PRIORITY_HIGH);
ntm.expectTestNotification(3, id2, PRIORITY_DEFAULT);
ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id1, PRIORITY_MIN);
AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
currentTime, AlertService.MAX_NOTIFICATIONS);
ntm.validateNotificationsAndReset();
// Test at 10:14am - next refresh should be 1 min later (15 min into event 'id4').
currentTime = Utils.createTimeInMillis(0, 14, 10, day, month, year,
Time.getCurrentTimezone());
alarmMgr.expectAlarmTime(AlarmManager.RTC, currentTime + 1 * DateUtils.MINUTE_IN_MILLIS);
ntm.expectTestNotification(1, id4, PRIORITY_HIGH);
ntm.expectTestNotification(2, id2, PRIORITY_DEFAULT);
ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, new int[] {id3, id1},
PRIORITY_MIN);
AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
currentTime, AlertService.MAX_NOTIFICATIONS);
ntm.validateNotificationsAndReset();
// Test at 10:15am - next refresh should be tomorrow midnight (end of all day event 'id2').
currentTime = Utils.createTimeInMillis(0, 15, 10, day, month, year,
Time.getCurrentTimezone());
alarmMgr.expectAlarmTime(AlarmManager.RTC, Utils.createTimeInMillis(0, 0, 23,
tomorrow.monthDay, tomorrow.month, tomorrow.year, Time.getCurrentTimezone()));
ntm.expectTestNotification(1, id2, PRIORITY_DEFAULT);
ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
new int[] {id4, id3, id1}, PRIORITY_MIN);
AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
currentTime, AlertService.MAX_NOTIFICATIONS);
ntm.validateNotificationsAndReset();
}
private NotificationInfo createNotificationInfo(long eventId) {
return new NotificationInfo("eventName", "location", "description", 100L, 200L, eventId,
false, false);
}
private static long createTimeInMillis(int hour, int minute) {
return Utils.createTimeInMillis(0 /* second */, minute, hour, 1 /* day */, 1 /* month */,
2012 /* year */, Time.getCurrentTimezone());
}
@SmallTest
public void testProcessQuery_skipDeclinedDismissed() {
int declinedEventId = 1;
int dismissedEventId = 2;
int acceptedEventId = 3;
long acceptedStartTime = createTimeInMillis(10, 0);
long acceptedEndTime = createTimeInMillis(10, 30);
AlertsTable at = new AlertsTable();
at.addAlertRow(declinedEventId, SCHEDULED, DECLINED, 0, createTimeInMillis(9, 0),
createTimeInMillis(10, 0), 0);
at.addAlertRow(dismissedEventId, SCHEDULED, DISMISSED, 0, createTimeInMillis(9, 30),
createTimeInMillis(11, 0), 0);
at.addAlertRow(acceptedEventId, SCHEDULED, ACCEPTED, 1, acceptedStartTime, acceptedEndTime,
0);
ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
long currentTime = createTimeInMillis(5, 0);
AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
mediumPriority, lowPriority);
assertEquals(0, lowPriority.size());
assertEquals(0, mediumPriority.size());
assertEquals(1, highPriority.size());
assertEquals(acceptedEventId, highPriority.get(0).eventId);
assertEquals(acceptedStartTime, highPriority.get(0).startMillis);
assertEquals(acceptedEndTime, highPriority.get(0).endMillis);
assertTrue(highPriority.get(0).allDay);
}
@SmallTest
public void testProcessQuery_newAlert() {
int scheduledAlertEventId = 1;
int firedAlertEventId = 2;
AlertsTable at = new AlertsTable();
at.addAlertRow(scheduledAlertEventId, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
createTimeInMillis(10, 0), 0);
at.addAlertRow(firedAlertEventId, FIRED, ACCEPTED, 0, createTimeInMillis(4, 0),
createTimeInMillis(10, 30), 0);
ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
long currentTime = createTimeInMillis(5, 0);
AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
mediumPriority, lowPriority);
assertEquals(0, lowPriority.size());
assertEquals(0, mediumPriority.size());
assertEquals(2, highPriority.size());
assertEquals(scheduledAlertEventId, highPriority.get(0).eventId);
assertTrue("newAlert should be ON for scheduled alerts", highPriority.get(0).newAlert);
assertEquals(firedAlertEventId, highPriority.get(1).eventId);
assertFalse("newAlert should be OFF for fired alerts", highPriority.get(1).newAlert);
}
@SmallTest
public void testProcessQuery_recurringEvent() {
int eventId = 1;
long earlierStartTime = createTimeInMillis(10, 0);
long laterStartTime = createTimeInMillis(11, 0);
ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
AlertsTable at = new AlertsTable();
at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 0, laterStartTime,
laterStartTime + DateUtils.HOUR_IN_MILLIS, 0);
at.addAlertRow(eventId, FIRED, ACCEPTED, 0, earlierStartTime,
earlierStartTime + DateUtils.HOUR_IN_MILLIS, 0);
// Both events in the future: the earliest one should be chosen.
long currentTime = earlierStartTime - DateUtils.DAY_IN_MILLIS * 5;
AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
mediumPriority, lowPriority);
assertEquals(0, lowPriority.size());
assertEquals(0, mediumPriority.size());
assertEquals(1, highPriority.size());
assertEquals("Recurring event with earlier start time expected", earlierStartTime,
highPriority.get(0).startMillis);
// Increment time just past the earlier event: the earlier one should be chosen.
highPriority.clear();
currentTime = earlierStartTime + DateUtils.MINUTE_IN_MILLIS * 10;
AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
mediumPriority, lowPriority);
assertEquals(0, lowPriority.size());
assertEquals(0, mediumPriority.size());
assertEquals(1, highPriority.size());
assertEquals("Recurring event with earlier start time expected", earlierStartTime,
highPriority.get(0).startMillis);
// Increment time to 15 min past the earlier event: the later one should be chosen.
highPriority.clear();
currentTime = earlierStartTime + DateUtils.MINUTE_IN_MILLIS * 15;
AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
mediumPriority, lowPriority);
assertEquals(0, lowPriority.size());
assertEquals(0, mediumPriority.size());
assertEquals(1, highPriority.size());
assertEquals("Recurring event with later start time expected", laterStartTime,
highPriority.get(0).startMillis);
// Both events in the past: the later one should be chosen (in the low priority bucket).
highPriority.clear();
currentTime = laterStartTime + DateUtils.DAY_IN_MILLIS * 5;
AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
mediumPriority, lowPriority);
assertEquals(0, highPriority.size());
assertEquals(0, mediumPriority.size());
assertEquals(1, lowPriority.size());
assertEquals("Recurring event with later start time expected", laterStartTime,
lowPriority.get(0).startMillis);
}
@SmallTest
public void testProcessQuery_recurringAllDayEvent() {
int eventId = 1;
long day1 = Utils.createTimeInMillis(0, 0, 0, 1, 5, 2012, Time.TIMEZONE_UTC);
long day2 = Utils.createTimeInMillis(0, 0, 0, 2, 5, 2012, Time.TIMEZONE_UTC);
ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
AlertsTable at = new AlertsTable();
at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 1, day2, day2 + DateUtils.HOUR_IN_MILLIS * 24,
0);
at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 1, day1, day1 + DateUtils.HOUR_IN_MILLIS * 24,
0);
// Both events in the future: the earliest one should be chosen.
long currentTime = day1 - DateUtils.DAY_IN_MILLIS * 3;
AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
mediumPriority, lowPriority);
assertEquals(0, lowPriority.size());
assertEquals(0, mediumPriority.size());
assertEquals(1, highPriority.size());
assertEquals("Recurring event with earlier start time expected", day1,
highPriority.get(0).startMillis);
// Increment time just past the earlier event (to 12:10am). The earlier one should
// be chosen.
highPriority.clear();
currentTime = Utils.createTimeInMillis(0, 10, 0, 1, 5, 2012, Time.getCurrentTimezone());
AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
mediumPriority, lowPriority);
assertEquals(0, lowPriority.size());
assertEquals(0, mediumPriority.size());
assertEquals(1, highPriority.size());
assertEquals("Recurring event with earlier start time expected", day1,
highPriority.get(0).startMillis);
// Increment time to 15 min past the earlier event: the later one should be chosen.
highPriority.clear();
currentTime = Utils.createTimeInMillis(0, 15, 0, 1, 5, 2012, Time.getCurrentTimezone());
AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
mediumPriority, lowPriority);
assertEquals(0, lowPriority.size());
assertEquals(0, mediumPriority.size());
assertEquals(1, highPriority.size());
assertEquals("Recurring event with earlier start time expected", day2,
highPriority.get(0).startMillis);
// Both events in the past: the later one should be chosen (in the low priority bucket).
highPriority.clear();
currentTime = day2 + DateUtils.DAY_IN_MILLIS * 1;
AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
mediumPriority, lowPriority);
assertEquals(0, highPriority.size());
assertEquals(0, mediumPriority.size());
assertEquals(1, lowPriority.size());
assertEquals("Recurring event with later start time expected", day2,
lowPriority.get(0).startMillis);
}
@SmallTest
public void testRedistributeBuckets_withinLimits() throws Exception {
int maxNotifications = 3;
ArrayList<NotificationInfo> threeItemList = new ArrayList<NotificationInfo>();
threeItemList.add(createNotificationInfo(5));
threeItemList.add(createNotificationInfo(4));
threeItemList.add(createNotificationInfo(3));
// Test when max notifications at high priority.
ArrayList<NotificationInfo> high = threeItemList;
ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
AlertService.redistributeBuckets(high, medium, low, maxNotifications);
assertEquals(3, high.size());
assertEquals(0, medium.size());
assertEquals(0, low.size());
// Test when max notifications at medium priority.
high = new ArrayList<NotificationInfo>();
medium = threeItemList;
low = new ArrayList<NotificationInfo>();
AlertService.redistributeBuckets(high, medium, low, maxNotifications);
assertEquals(0, high.size());
assertEquals(3, medium.size());
assertEquals(0, low.size());
// Test when max notifications at high and medium priority
high = new ArrayList<NotificationInfo>(threeItemList);
medium = new ArrayList<NotificationInfo>();
medium.add(high.remove(1));
low = new ArrayList<NotificationInfo>();
AlertService.redistributeBuckets(high, medium, low, maxNotifications);
assertEquals(2, high.size());
assertEquals(1, medium.size());
assertEquals(0, low.size());
}
@SmallTest
public void testRedistributeBuckets_tooManyHighPriority() throws Exception {
ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
high.add(createNotificationInfo(5));
high.add(createNotificationInfo(4));
high.add(createNotificationInfo(3));
high.add(createNotificationInfo(2));
high.add(createNotificationInfo(1));
// Invoke the method under test.
int maxNotifications = 3;
AlertService.redistributeBuckets(high, medium, low, maxNotifications);
// Verify some high priority were kicked out.
assertEquals(3, high.size());
assertEquals(3, high.get(0).eventId);
assertEquals(2, high.get(1).eventId);
assertEquals(1, high.get(2).eventId);
// Verify medium priority untouched.
assertEquals(0, medium.size());
// Verify the extras went to low priority.
assertEquals(2, low.size());
assertEquals(5, low.get(0).eventId);
assertEquals(4, low.get(1).eventId);
}
@SmallTest
public void testRedistributeBuckets_tooManyMediumPriority() throws Exception {
ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
high.add(createNotificationInfo(5));
high.add(createNotificationInfo(4));
medium.add(createNotificationInfo(3));
medium.add(createNotificationInfo(2));
medium.add(createNotificationInfo(1));
// Invoke the method under test.
int maxNotifications = 3;
AlertService.redistributeBuckets(high, medium, low, maxNotifications);
// Verify high priority untouched.
assertEquals(2, high.size());
assertEquals(5, high.get(0).eventId);
assertEquals(4, high.get(1).eventId);
// Verify some medium priority were kicked out (the ones near the end of the
// list).
assertEquals(1, medium.size());
assertEquals(3, medium.get(0).eventId);
// Verify the extras went to low priority.
assertEquals(2, low.size());
assertEquals(2, low.get(0).eventId);
assertEquals(1, low.get(1).eventId);
}
@SmallTest
public void testRedistributeBuckets_tooManyHighMediumPriority() throws Exception {
ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
high.add(createNotificationInfo(8));
high.add(createNotificationInfo(7));
high.add(createNotificationInfo(6));
high.add(createNotificationInfo(5));
high.add(createNotificationInfo(4));
medium.add(createNotificationInfo(3));
medium.add(createNotificationInfo(2));
medium.add(createNotificationInfo(1));
// Invoke the method under test.
int maxNotifications = 3;
AlertService.redistributeBuckets(high, medium, low, maxNotifications);
// Verify high priority.
assertEquals(3, high.size());
assertEquals(6, high.get(0).eventId);
assertEquals(5, high.get(1).eventId);
assertEquals(4, high.get(2).eventId);
// Verify some medium priority.
assertEquals(0, medium.size());
// Verify low priority.
assertEquals(5, low.size());
assertEquals(8, low.get(0).eventId);
assertEquals(7, low.get(1).eventId);
assertEquals(3, low.get(2).eventId);
assertEquals(2, low.get(3).eventId);
assertEquals(1, low.get(4).eventId);
}
}