/* * 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.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 { /* "always", "silent", depends on ringer mode */ private String mVibrateWhen; 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() { mVibrateWhen = "always"; mRingtone = "/some/cool/ringtone"; mPopup = true; } @Override public boolean contains(String key) { if (GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN.equals(key)) { return true; } return false; } @Override public boolean getBoolean(String key, boolean defValue) { 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_VIBRATE_WHEN.equals(key)) { if (mVibrateWhen == null) { Assert.fail(GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN + " fetched more than once."); } String val = mVibrateWhen; if (mStrict) { mVibrateWhen = null; } return val; } 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 getAll() { throw new IllegalArgumentException(); } @Override public Set getStringSet(String key, Set 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 mAlerts = new ArrayList(); 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 implements NotificationMgr { // Expected notifications NotificationInstance[] mNotifications; // Flag to know which notification has been posted or canceled boolean[] mDone; // CalendarAlerts table private ArrayList mAlerts; public NotificationTestManager(ArrayList alerts, int maxNotifications) { assertEquals(0, AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID); mAlerts = alerts; mNotifications = new NotificationInstance[maxNotifications + 1]; } public void expectTestNotification(int notificationId, int alertId, int highPriority) { mNotifications[notificationId] = new NotificationInstance(alertId, highPriority); } public void expectTestNotification(int notificationId, int[] alertIds, int priority) { mNotifications[notificationId] = new NotificationInstance(alertIds, priority); } private void verifyNotification(int id, NotificationWrapper nw) { assertEquals(mNotifications[id].mPriority, nw.mNotification.priority); NotificationInstance expected = mNotifications[id]; if (expected.mAlertIdsInDigest == null) { Alert a = mAlerts.get(expected.mAlertId); assertEquals("Event ID", a.mEventId, nw.mEventId); assertEquals("Begin time", a.mBegin, nw.mBegin); assertEquals("End time", a.mEnd, nw.mEnd); } else { // Notification should be a digest. assertNotNull("Posted notification not a digest as expected.", nw.mNw); assertEquals("Number of notifications in digest not as expected.", expected.mAlertIdsInDigest.length, nw.mNw.size()); for (int i = 0; i < nw.mNw.size(); i++) { Alert a = mAlerts.get(expected.mAlertIdsInDigest[i]); assertEquals("Digest item " + i + ": Event ID not as expected", a.mEventId, nw.mNw.get(i).mEventId); assertEquals("Digest item " + i + ": Begin time in digest", a.mBegin, nw.mNw.get(i).mBegin); assertEquals("Digest item " + i + ": End time in digest", a.mEnd, nw.mNw.get(i).mEnd); } } } public void validateNotificationsAndReset() { for (int i = 0; i < mDone.length; i++) { assertTrue("Notification id " + i + " has not been posted", mDone[i]); } Arrays.fill(mDone, false); Arrays.fill(mNotifications, null); } /////////////////////////////// // NotificationMgr methods @Override public void cancel(int id) { if (mDone == null) { mDone = new boolean[mNotifications.length]; } assertTrue("id out of bound: " + id, 0 <= id); assertTrue("id out of bound: " + id, id < mDone.length); assertFalse("id already used", mDone[id]); mDone[id] = true; assertNull("Unexpected cancel for id " + id, mNotifications[id]); } @Override public void cancel(String tag, int id) { throw new IllegalArgumentException(); } @Override public void cancelAll() { for (int i = 0; i < mNotifications.length; i++) { assertNull("Expecting notification id " + i + ". Got cancelAll", mNotifications[i]); if (mDone != null) { assertFalse("Notification id " + i + " is done but got cancelAll", mDone[i]); } } assertNull(mDone); // this should have been null since nothing // should have been posted mDone = new boolean[mNotifications.length]; Arrays.fill(mDone, true); } @Override public void notify(int id, NotificationWrapper nw) { if (mDone == null) { mDone = new boolean[mNotifications.length]; } assertTrue("id out of bound: " + id, 0 <= id); assertTrue("id out of bound: " + id, id < mDone.length); assertFalse("id already used", mDone[id]); mDone[id] = true; assertNotNull("Unexpected notify for id " + id, mNotifications[id]); verifyNotification(id, nw); } @Override public void notify(String tag, int id, NotificationWrapper nw) { throw new IllegalArgumentException(); } } // TODO // Catch updates of new state, notify time, and received time // Test ringer, vibrate, // Test digest notifications // Test intents, action email // Catch alarmmgr calls @Smoke @SmallTest public void testNoAlerts() { 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, prefs, at.getAlertCursor(), currentTime, AlertService.MAX_NOTIFICATIONS); ntm.validateNotificationsAndReset(); } @Smoke @SmallTest public void testSingleAlert() { MockSharedPreferences prefs = new MockSharedPreferences(); 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, 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_DEFAULT); AlertService.generateAlerts(mContext, ntm, 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, prefs, at.getAlertCursor(), currentTime, AlertService.MAX_NOTIFICATIONS); ntm.validateNotificationsAndReset(); } @SmallTest public void testMultipleAlerts() { int maxNotifications = 10; MockSharedPreferences prefs = new MockSharedPreferences(); 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, 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, 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, prefs, at.getAlertCursor(), currentTime, maxNotifications); ntm.validateNotificationsAndReset(); } @SmallTest public void testMultipleAlerts_max() { MockSharedPreferences prefs = new MockSharedPreferences(); 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, 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, prefs, at.getAlertCursor(), currentTime, maxNotifications); ntm.validateNotificationsAndReset(); } /** * Test that the SharedPreferences are only fetched once for each setting. */ @SmallTest public void testMultipleAlerts_sharedPreferences() { int maxNotifications = 10; MockSharedPreferences prefs = new MockSharedPreferences(true /* strict mode */); 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); // Expected notifications. 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); // If this does not result in a failure (MockSharedPreferences fails for duplicate // queries), then test passes. AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, maxNotifications); 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 createTimeInMillis(0 /* second */, minute, hour, 1 /* day */, 1 /* month */, 2012 /* year */, Time.getCurrentTimezone()); } private static long createTimeInMillis(int second, int minute, int hour, int monthDay, int month, int year, String timezone) { Time t = new Time(timezone); t.set(second, minute, hour, monthDay, month, year); t.normalize(false); return t.toMillis(false); } @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 highPriority = new ArrayList(); ArrayList mediumPriority = new ArrayList(); ArrayList lowPriority = new ArrayList(); 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(10, 0), createTimeInMillis(10, 30), 0); ArrayList highPriority = new ArrayList(); ArrayList mediumPriority = new ArrayList(); ArrayList lowPriority = new ArrayList(); 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 highPriority = new ArrayList(); ArrayList mediumPriority = new ArrayList(); ArrayList lowPriority = new ArrayList(); 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 = createTimeInMillis(0, 0, 0, 1, 5, 2012, Time.TIMEZONE_UTC); long day2 = createTimeInMillis(0, 0, 0, 2, 5, 2012, Time.TIMEZONE_UTC); ArrayList highPriority = new ArrayList(); ArrayList mediumPriority = new ArrayList(); ArrayList lowPriority = new ArrayList(); 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 = 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 = 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 threeItemList = new ArrayList(); threeItemList.add(createNotificationInfo(5)); threeItemList.add(createNotificationInfo(4)); threeItemList.add(createNotificationInfo(3)); // Test when max notifications at high priority. ArrayList high = threeItemList; ArrayList medium = new ArrayList(); ArrayList low = new ArrayList(); 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(); medium = threeItemList; low = new ArrayList(); 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(threeItemList); medium = new ArrayList(); medium.add(high.remove(1)); low = new ArrayList(); 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 high = new ArrayList(); ArrayList medium = new ArrayList(); ArrayList low = new ArrayList(); 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 high = new ArrayList(); ArrayList medium = new ArrayList(); ArrayList low = new ArrayList(); 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 high = new ArrayList(); ArrayList medium = new ArrayList(); ArrayList low = new ArrayList(); 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); } }