1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.android.calendar.alerts;
18 
19 import static android.app.Notification.PRIORITY_DEFAULT;
20 import static android.app.Notification.PRIORITY_HIGH;
21 import static android.app.Notification.PRIORITY_MIN;
22 
23 import android.app.AlarmManager;
24 import android.content.SharedPreferences;
25 import android.database.MatrixCursor;
26 import android.provider.CalendarContract.Attendees;
27 import android.provider.CalendarContract.CalendarAlerts;
28 import android.test.AndroidTestCase;
29 import android.test.suitebuilder.annotation.SmallTest;
30 import android.test.suitebuilder.annotation.Smoke;
31 import android.text.format.DateUtils;
32 import android.text.format.Time;
33 
34 import com.android.calendar.GeneralPreferences;
35 import com.android.calendar.alerts.AlertService.NotificationInfo;
36 import com.android.calendar.alerts.AlertService.NotificationWrapper;
37 
38 import junit.framework.Assert;
39 
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Map;
43 import java.util.Set;
44 
45 public class AlertServiceTest extends AndroidTestCase {
46 
47     class MockSharedPreferences implements SharedPreferences {
48 
49         private Boolean mVibrate;
50         private String mRingtone;
51         private Boolean mPopup;
52 
53         // Strict mode will fail if a preference key is queried more than once.
54         private boolean mStrict = false;
55 
MockSharedPreferences()56         MockSharedPreferences() {
57             this(false);
58         }
59 
MockSharedPreferences(boolean strict)60         MockSharedPreferences(boolean strict) {
61             super();
62             init();
63             this.mStrict = strict;
64         }
65 
init()66         void init() {
67             mVibrate = true;
68             mRingtone = "/some/cool/ringtone";
69             mPopup = true;
70         }
71 
72         @Override
contains(String key)73         public boolean contains(String key) {
74             if (GeneralPreferences.KEY_ALERTS_VIBRATE.equals(key)) {
75                 return true;
76             }
77             return false;
78         }
79 
80         @Override
getBoolean(String key, boolean defValue)81         public boolean getBoolean(String key, boolean defValue) {
82             if (GeneralPreferences.KEY_ALERTS_VIBRATE.equals(key)) {
83                 if (mVibrate == null) {
84                     Assert.fail(GeneralPreferences.KEY_ALERTS_VIBRATE
85                             + " fetched more than once.");
86                 }
87                 boolean val = mVibrate;
88                 if (mStrict) {
89                     mVibrate = null;
90                 }
91                 return val;
92             }
93             if (GeneralPreferences.KEY_ALERTS_POPUP.equals(key)) {
94                 if (mPopup == null) {
95                     Assert.fail(GeneralPreferences.KEY_ALERTS_POPUP + " fetched more than once.");
96                 }
97                 boolean val = mPopup;
98                 if (mStrict) {
99                     mPopup = null;
100                 }
101                 return val;
102             }
103             throw new IllegalArgumentException();
104         }
105 
106         @Override
getString(String key, String defValue)107         public String getString(String key, String defValue) {
108             if (GeneralPreferences.KEY_ALERTS_RINGTONE.equals(key)) {
109                 if (mRingtone == null) {
110                     Assert.fail(GeneralPreferences.KEY_ALERTS_RINGTONE
111                             + " fetched more than once.");
112                 }
113                 String val = mRingtone;
114                 if (mStrict) {
115                     mRingtone = null;
116                 }
117                 return val;
118             }
119             throw new IllegalArgumentException();
120         }
121 
122         @Override
getAll()123         public Map<String, ?> getAll() {
124             throw new IllegalArgumentException();
125         }
126 
127         @Override
getStringSet(String key, Set<String> defValues)128         public Set<String> getStringSet(String key, Set<String> defValues) {
129             throw new IllegalArgumentException();
130         }
131 
132         @Override
getInt(String key, int defValue)133         public int getInt(String key, int defValue) {
134             throw new IllegalArgumentException();
135         }
136 
137         @Override
getLong(String key, long defValue)138         public long getLong(String key, long defValue) {
139             throw new IllegalArgumentException();
140         }
141 
142         @Override
getFloat(String key, float defValue)143         public float getFloat(String key, float defValue) {
144             throw new IllegalArgumentException();
145         }
146 
147         @Override
edit()148         public Editor edit() {
149             throw new IllegalArgumentException();
150         }
151 
152         @Override
registerOnSharedPreferenceChangeListener( OnSharedPreferenceChangeListener listener)153         public void registerOnSharedPreferenceChangeListener(
154                 OnSharedPreferenceChangeListener listener) {
155             throw new IllegalArgumentException();
156         }
157 
158         @Override
unregisterOnSharedPreferenceChangeListener( OnSharedPreferenceChangeListener listener)159         public void unregisterOnSharedPreferenceChangeListener(
160                 OnSharedPreferenceChangeListener listener) {
161             throw new IllegalArgumentException();
162         }
163 
164     }
165 
166     // Created these constants so the test cases are shorter
167     public static final int SCHEDULED = CalendarAlerts.STATE_SCHEDULED;
168     public static final int FIRED = CalendarAlerts.STATE_FIRED;
169     public static final int DISMISSED = CalendarAlerts.STATE_DISMISSED;
170 
171     public static final int ACCEPTED = Attendees.ATTENDEE_STATUS_ACCEPTED;
172     public static final int DECLINED = Attendees.ATTENDEE_STATUS_DECLINED;
173     public static final int INVITED = Attendees.ATTENDEE_STATUS_INVITED;
174     public static final int TENTATIVE = Attendees.ATTENDEE_STATUS_TENTATIVE;
175 
176     class NotificationInstance {
177         int mAlertId;
178         int[] mAlertIdsInDigest;
179         int mPriority;
180 
NotificationInstance(int alertId, int priority)181         public NotificationInstance(int alertId, int priority) {
182             mAlertId = alertId;
183             mPriority = priority;
184         }
185 
NotificationInstance(int[] alertIdsInDigest, int priority)186         public NotificationInstance(int[] alertIdsInDigest, int priority) {
187             mAlertIdsInDigest = alertIdsInDigest;
188             mPriority = priority;
189         }
190     }
191 
192     class Alert {
193         long mEventId;
194         int mAlertStatus;
195         int mResponseStatus;
196         int mAllDay;
197         long mBegin;
198         long mEnd;
199         int mMinute;
200         long mAlarmTime;
201 
Alert(long eventId, int alertStatus, int responseStatus, int allDay, long begin, long end, int minute, long alarmTime)202         public Alert(long eventId, int alertStatus, int responseStatus, int allDay, long begin,
203                 long end, int minute, long alarmTime) {
204             mEventId = eventId;
205             mAlertStatus = alertStatus;
206             mResponseStatus = responseStatus;
207             mAllDay = allDay;
208             mBegin = begin;
209             mEnd = end;
210             mMinute = minute;
211             mAlarmTime = alarmTime;
212         }
213 
214     }
215 
216     class AlertsTable {
217 
218         ArrayList<Alert> mAlerts = new ArrayList<Alert>();
219 
addAlertRow(long eventId, int alertStatus, int responseStatus, int allDay, long begin, long end, long alarmTime)220         int addAlertRow(long eventId, int alertStatus, int responseStatus, int allDay, long begin,
221                 long end, long alarmTime) {
222             Alert a = new Alert(eventId, alertStatus, responseStatus, allDay, begin, end,
223                     5 /* minute */, alarmTime);
224             int id = mAlerts.size();
225             mAlerts.add(a);
226             return id;
227         }
228 
getAlertCursor()229         public MatrixCursor getAlertCursor() {
230             MatrixCursor alertCursor = new MatrixCursor(AlertService.ALERT_PROJECTION);
231 
232             int i = 0;
233             for (Alert a : mAlerts) {
234                 Object[] ca = {
235                         i++,
236                         a.mEventId,
237                         a.mAlertStatus,
238                         "Title" + a.mEventId + " " + a.mMinute,
239                         "Loc" + a.mEventId,
240                         a.mResponseStatus,
241                         a.mAllDay,
242                         a.mAlarmTime > 0 ? a.mAlarmTime : a.mBegin - a.mMinute * 60 * 1000,
243                         a.mMinute,
244                         a.mBegin,
245                         a.mEnd,
246                         "Desc: " + a.mAlarmTime
247                 };
248                 alertCursor.addRow(ca);
249             }
250             return alertCursor;
251         }
252 
253     }
254 
255     class NotificationTestManager extends NotificationMgr {
256         // Expected notifications
257         NotificationInstance[] mExpectedNotifications;
258         NotificationWrapper[] mActualNotifications;
259         boolean[] mCancelled;
260 
261         // CalendarAlerts table
262         private ArrayList<Alert> mAlerts;
263 
NotificationTestManager(ArrayList<Alert> alerts, int maxNotifications)264         public NotificationTestManager(ArrayList<Alert> alerts, int maxNotifications) {
265             assertEquals(0, AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID);
266             mAlerts = alerts;
267             mExpectedNotifications = new NotificationInstance[maxNotifications + 1];
268             mActualNotifications = new NotificationWrapper[mExpectedNotifications.length];
269             mCancelled = new boolean[mExpectedNotifications.length];
270         }
271 
expectTestNotification(int notificationId, int alertId, int highPriority)272         public void expectTestNotification(int notificationId, int alertId, int highPriority) {
273             mExpectedNotifications[notificationId] = new NotificationInstance(alertId,
274                     highPriority);
275         }
276 
expectTestNotification(int notificationId, int[] alertIds, int priority)277         public void expectTestNotification(int notificationId, int[] alertIds, int priority) {
278             mExpectedNotifications[notificationId] = new NotificationInstance(alertIds, priority);
279         }
280 
nullContents(T[] array)281         private <T> boolean nullContents(T[] array) {
282             for (T item : array) {
283                 if (item != null) {
284                     return false;
285                 }
286             }
287             return true;
288         }
289 
validateNotificationsAndReset()290         public void validateNotificationsAndReset() {
291             if (nullContents(mExpectedNotifications)) {
292                 return;
293             }
294 
295             String debugStr = printActualNotifications();
296             for (int id = 0; id < mActualNotifications.length; id++) {
297                 NotificationInstance expected = mExpectedNotifications[id];
298                 NotificationWrapper actual = mActualNotifications[id];
299                 if (expected == null) {
300                     assertNull("Received unexpected notificationId " + id + debugStr, actual);
301                     assertTrue("NotificationId " + id + " should have been cancelled." + debugStr,
302                             mCancelled[id]);
303                 } else {
304                     assertNotNull("Expected notificationId " + id + " but it was not posted."
305                             + debugStr, actual);
306                     assertFalse("NotificationId " + id + " should not have been cancelled."
307                             + debugStr, mCancelled[id]);
308                     assertEquals("Priority not as expected for notification " + id + debugStr,
309                             expected.mPriority, actual.mNotification.priority);
310                     if (expected.mAlertIdsInDigest == null) {
311                         Alert a = mAlerts.get(expected.mAlertId);
312                         assertEquals("Event ID not expected for notification " + id + debugStr,
313                                 a.mEventId, actual.mEventId);
314                         assertEquals("Begin time not expected for notification " + id + debugStr,
315                                 a.mBegin, actual.mBegin);
316                         assertEquals("End time not expected for notification " + id + debugStr,
317                                 a.mEnd, actual.mEnd);
318                     } else {
319                         // Notification should be a digest.
320                         assertNotNull("Posted notification not a digest as expected." + debugStr,
321                                 actual.mNw);
322                         assertEquals("Number of notifications in digest not as expected."
323                                 + debugStr, expected.mAlertIdsInDigest.length, actual.mNw.size());
324                         for (int i = 0; i < actual.mNw.size(); i++) {
325                             Alert a = mAlerts.get(expected.mAlertIdsInDigest[i]);
326                             assertEquals("Digest item " + i + ": Event ID not as expected"
327                                     + debugStr, a.mEventId, actual.mNw.get(i).mEventId);
328                             assertEquals("Digest item " + i + ": Begin time in digest not expected"
329                                     + debugStr, a.mBegin, actual.mNw.get(i).mBegin);
330                             assertEquals("Digest item " + i + ": End time in digest not expected"
331                                     + debugStr, a.mEnd, actual.mNw.get(i).mEnd);
332                         }
333                     }
334                 }
335             }
336 
337             Arrays.fill(mCancelled, false);
338             Arrays.fill(mExpectedNotifications, null);
339             Arrays.fill(mActualNotifications, null);
340         }
341 
printActualNotifications()342         private String printActualNotifications() {
343             StringBuilder s = new StringBuilder();
344             s.append("\n\nNotifications actually posted:\n");
345             for (int i = mActualNotifications.length - 1; i >= 0; i--) {
346                 NotificationWrapper actual = mActualNotifications[i];
347                 if (actual == null) {
348                     continue;
349                 }
350                 s.append("Notification " + i + " -- ");
351                 s.append("priority:" + actual.mNotification.priority);
352                 if (actual.mNw == null) {
353                     s.append(", eventId:" +  actual.mEventId);
354                 } else {
355                     s.append(", eventIds:{");
356                     for (int digestIndex = 0; digestIndex < actual.mNw.size(); digestIndex++) {
357                         s.append(actual.mNw.get(digestIndex).mEventId + ",");
358                     }
359                     s.append("}");
360                 }
361                 s.append("\n");
362             }
363             return s.toString();
364         }
365 
366         ///////////////////////////////
367         // NotificationMgr methods
368         @Override
cancel(int id)369         public void cancel(int id) {
370             assertTrue("id out of bound: " + id, 0 <= id);
371             assertTrue("id out of bound: " + id, id < mCancelled.length);
372             assertNull("id already used", mActualNotifications[id]);
373             assertFalse("id already used", mCancelled[id]);
374             mCancelled[id] = true;
375             assertNull("Unexpected cancel for id " + id, mExpectedNotifications[id]);
376         }
377 
378         @Override
notify(int id, NotificationWrapper nw)379         public void notify(int id, NotificationWrapper nw) {
380             assertTrue("id out of bound: " + id, 0 <= id);
381             assertTrue("id out of bound: " + id, id < mExpectedNotifications.length);
382             assertNull("id already used: " + id, mActualNotifications[id]);
383             mActualNotifications[id] = nw;
384         }
385     }
386 
387     // TODO
388     // Catch updates of new state, notify time, and received time
389     // Test ringer, vibrate,
390     // Test intents, action email
391 
392     @Smoke
393     @SmallTest
testGenerateAlerts_none()394     public void testGenerateAlerts_none() {
395         MockSharedPreferences prefs = new MockSharedPreferences();
396         AlertsTable at = new AlertsTable();
397         NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
398                 AlertService.MAX_NOTIFICATIONS);
399 
400         // Test no alert
401         long currentTime = 1000000;
402         AlertService.generateAlerts(mContext, ntm, new MockAlarmManager(mContext), prefs,
403                 at.getAlertCursor(), currentTime, AlertService.MAX_NOTIFICATIONS);
404         ntm.validateNotificationsAndReset();
405     }
406 
407     @Smoke
408     @SmallTest
testGenerateAlerts_single()409     public void testGenerateAlerts_single() {
410         MockSharedPreferences prefs = new MockSharedPreferences();
411         MockAlarmManager alarmMgr = new MockAlarmManager(mContext);
412         AlertsTable at = new AlertsTable();
413         NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
414                 AlertService.MAX_NOTIFICATIONS);
415 
416         int id = at.addAlertRow(100, SCHEDULED, ACCEPTED, 0 /* all day */, 1300000, 2300000, 0);
417 
418         // Test one up coming alert
419         long currentTime = 1000000;
420         ntm.expectTestNotification(1, id, PRIORITY_HIGH);
421 
422         AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(), currentTime,
423                 AlertService.MAX_NOTIFICATIONS);
424         ntm.validateNotificationsAndReset(); // This wipes out notification
425                                              // tests added so far
426 
427         // Test half way into an event
428         currentTime = 2300000;
429         ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id, PRIORITY_MIN);
430 
431         AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(), currentTime,
432                 AlertService.MAX_NOTIFICATIONS);
433         ntm.validateNotificationsAndReset();
434 
435         // Test event ended
436         currentTime = 4300000;
437         ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id, PRIORITY_MIN);
438 
439         AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(), currentTime,
440                 AlertService.MAX_NOTIFICATIONS);
441         ntm.validateNotificationsAndReset();
442     }
443 
444     @SmallTest
testGenerateAlerts_multiple()445     public void testGenerateAlerts_multiple() {
446         int maxNotifications = 10;
447         MockSharedPreferences prefs = new MockSharedPreferences();
448         MockAlarmManager alarmMgr = new MockAlarmManager(mContext);
449         AlertsTable at = new AlertsTable();
450         NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications);
451 
452         // Current time - 5:00
453         long currentTime = createTimeInMillis(5, 0);
454 
455         // Set up future alerts.  The real query implementation sorts by descending start
456         // time so simulate that here with our order of adds to AlertsTable.
457         int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
458                 createTimeInMillis(10, 0), 0);
459         int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0),
460                 createTimeInMillis(9, 0), 0);
461         int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0),
462                 createTimeInMillis(8, 0), 0);
463 
464         // Set up concurrent alerts (that started recently).
465         int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0),
466                 createTimeInMillis(5, 40), 0);
467         int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55),
468                 createTimeInMillis(7, 30), 0);
469         int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50),
470                 createTimeInMillis(4, 50), 0);
471 
472         // Set up past alerts.
473         int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0),
474                 createTimeInMillis(4, 0), 0);
475         int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0),
476                 createTimeInMillis(3, 0), 0);
477         int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0),
478                 createTimeInMillis(2, 0), 0);
479 
480         // Check posted notifications.  The order listed here is the order simulates the
481         // order in the real notification bar (last one posted appears on top), so these
482         // should be lowest start time on top.
483         ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent
484         ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent
485         ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent
486         ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future
487         ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future
488         ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
489         ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
490                 new int[] {id3, id2, id1}, PRIORITY_MIN);
491         AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
492                 currentTime, maxNotifications);
493         ntm.validateNotificationsAndReset();
494 
495         // Increase time by 15 minutes to check that some concurrent events dropped
496         // to the low priority bucket.
497         currentTime = createTimeInMillis(5, 15);
498         ntm.expectTestNotification(4, id5, PRIORITY_HIGH); // concurrent
499         ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future
500         ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future
501         ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
502         ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
503                 new int[] {id6, id4, id3, id2, id1}, PRIORITY_MIN);
504         AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
505                 currentTime, maxNotifications);
506         ntm.validateNotificationsAndReset();
507 
508         // Increase time so some of the previously future ones change state.
509         currentTime = createTimeInMillis(8, 15);
510         ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
511         ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
512                 new int[] {id8, id7, id6, id5, id4, id3, id2, id1}, PRIORITY_MIN);
513         AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
514                 currentTime, maxNotifications);
515         ntm.validateNotificationsAndReset();
516     }
517 
518     @SmallTest
testGenerateAlerts_maxAlerts()519     public void testGenerateAlerts_maxAlerts() {
520         MockSharedPreferences prefs = new MockSharedPreferences();
521         MockAlarmManager alarmMgr = new MockAlarmManager(mContext);
522         AlertsTable at = new AlertsTable();
523 
524         // Current time - 5:00
525         long currentTime = createTimeInMillis(5, 0);
526 
527         // Set up future alerts.  The real query implementation sorts by descending start
528         // time so simulate that here with our order of adds to AlertsTable.
529         int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
530                 createTimeInMillis(10, 0), 0);
531         int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0),
532                 createTimeInMillis(9, 0), 0);
533         int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0),
534                 createTimeInMillis(8, 0), 0);
535 
536         // Set up concurrent alerts (that started recently).
537         int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0),
538                 createTimeInMillis(5, 40), 0);
539         int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55),
540                 createTimeInMillis(7, 30), 0);
541         int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50),
542                 createTimeInMillis(4, 50), 0);
543 
544         // Set up past alerts.
545         int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0),
546                 createTimeInMillis(4, 0), 0);
547         int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0),
548                 createTimeInMillis(3, 0), 0);
549         int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0),
550                 createTimeInMillis(2, 0), 0);
551 
552         // Test when # alerts = max.
553         int maxNotifications = 6;
554         NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications);
555         ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent
556         ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent
557         ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent
558         ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future
559         ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future
560         ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
561         ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
562                 new int[] {id3, id2, id1}, PRIORITY_MIN);
563         AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
564                 currentTime, maxNotifications);
565         ntm.validateNotificationsAndReset();
566 
567         // Test when # alerts > max.
568         maxNotifications = 4;
569         ntm = new NotificationTestManager(at.mAlerts, maxNotifications);
570         ntm.expectTestNotification(4, id4, PRIORITY_HIGH); // concurrent
571         ntm.expectTestNotification(3, id5, PRIORITY_HIGH); // concurrent
572         ntm.expectTestNotification(2, id6, PRIORITY_HIGH); // concurrent
573         ntm.expectTestNotification(1, id7, PRIORITY_HIGH); // future
574         ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
575                 new int[] {id9, id8, id3, id2, id1}, PRIORITY_MIN);
576         AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
577                 currentTime, maxNotifications);
578         ntm.validateNotificationsAndReset();
579     }
580 
581     /**
582      * Test that the SharedPreferences are only fetched once for each setting.
583      */
584     @SmallTest
testGenerateAlerts_sharedPreferences()585     public void testGenerateAlerts_sharedPreferences() {
586         MockSharedPreferences prefs = new MockSharedPreferences(true /* strict mode */);
587         AlertsTable at = new AlertsTable();
588         NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
589                 AlertService.MAX_NOTIFICATIONS);
590 
591         // Current time - 5:00
592         long currentTime = createTimeInMillis(5, 0);
593 
594         // Set up future alerts.  The real query implementation sorts by descending start
595         // time so simulate that here with our order of adds to AlertsTable.
596         at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
597                 createTimeInMillis(10, 0), 0);
598         at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0),
599                 createTimeInMillis(9, 0), 0);
600         at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0),
601                 createTimeInMillis(8, 0), 0);
602 
603         // If this does not result in a failure (MockSharedPreferences fails for duplicate
604         // queries), then test passes.
605         AlertService.generateAlerts(mContext, ntm, new MockAlarmManager(mContext), prefs,
606                 at.getAlertCursor(), currentTime, AlertService.MAX_NOTIFICATIONS);
607     }
608 
testGenerateAlerts_refreshTime()609     public void testGenerateAlerts_refreshTime() {
610         AlertsTable at = new AlertsTable();
611         MockSharedPreferences prefs = new MockSharedPreferences();
612         MockAlarmManager alarmMgr = new MockAlarmManager(mContext);
613         NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
614                 AlertService.MAX_NOTIFICATIONS);
615 
616         // Since AlertService.processQuery uses DateUtils.isToday instead of checking against
617         // the passed in currentTime (not worth allocating the extra Time objects to do so), use
618         // today's date for this test.
619         Time now = new Time();
620         now.setToNow();
621         int day = now.monthDay;
622         int month = now.month;
623         int year = now.year;
624         Time yesterday = new Time();
625         yesterday.set(System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS);
626         Time tomorrow = new Time();
627         tomorrow.set(System.currentTimeMillis() + DateUtils.DAY_IN_MILLIS);
628         long allDayStart = Utils.createTimeInMillis(0, 0, 0, day, month, year, Time.TIMEZONE_UTC);
629 
630         /* today 10am - 10:30am */
631         int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0,
632                 Utils.createTimeInMillis(0, 0, 10, day, month, year, Time.getCurrentTimezone()),
633                 Utils.createTimeInMillis(0, 30, 10, day, month, year, Time.getCurrentTimezone()),
634                         0);
635         /* today 6am - 6am (0 duration event) */
636         int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0,
637                 Utils.createTimeInMillis(0, 0, 6, day, month, year, Time.getCurrentTimezone()),
638                 Utils.createTimeInMillis(0, 0, 6, day, month, year, Time.getCurrentTimezone()), 0);
639         /* today allDay */
640         int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 1, allDayStart,
641                 allDayStart + DateUtils.HOUR_IN_MILLIS * 24, 0);
642         /* yesterday 11pm - today 7am (multiday event) */
643         int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0,
644                 Utils.createTimeInMillis(0, 0, 23, yesterday.monthDay, yesterday.month,
645                         yesterday.year, Time.getCurrentTimezone()),
646                 Utils.createTimeInMillis(0, 0, 7, day, month, year, Time.getCurrentTimezone()), 0);
647 
648         // Test at midnight - next refresh should be 15 min later (15 min into the all
649         // day event).
650         long currentTime = Utils.createTimeInMillis(0, 0, 0, day, month, year,
651                 Time.getCurrentTimezone());
652         alarmMgr.expectAlarmTime(AlarmManager.RTC, currentTime + 15 * DateUtils.MINUTE_IN_MILLIS);
653         ntm.expectTestNotification(4, id1, PRIORITY_HIGH);
654         ntm.expectTestNotification(3, id2, PRIORITY_HIGH);
655         ntm.expectTestNotification(2, id3, PRIORITY_HIGH);
656         ntm.expectTestNotification(1, id4, PRIORITY_HIGH);
657         AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
658                 currentTime, AlertService.MAX_NOTIFICATIONS);
659         ntm.validateNotificationsAndReset();
660 
661         // Test at 12:30am - next refresh should be 30 min later (1/4 into event 'id1').
662         currentTime = Utils.createTimeInMillis(0, 30, 0, day, month, year,
663                 Time.getCurrentTimezone());
664         alarmMgr.expectAlarmTime(AlarmManager.RTC, currentTime + 30 * DateUtils.MINUTE_IN_MILLIS);
665         ntm.expectTestNotification(3, id1, PRIORITY_HIGH);
666         ntm.expectTestNotification(2, id3, PRIORITY_HIGH);
667         ntm.expectTestNotification(1, id4, PRIORITY_HIGH);
668         ntm.expectTestNotification(4, id2, PRIORITY_DEFAULT);
669         AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
670                 currentTime, AlertService.MAX_NOTIFICATIONS);
671         ntm.validateNotificationsAndReset();
672 
673         // Test at 5:55am - next refresh should be 20 min later (15 min after 'id3').
674         currentTime = Utils.createTimeInMillis(0, 55, 5, day, month, year,
675                 Time.getCurrentTimezone());
676         alarmMgr.expectAlarmTime(AlarmManager.RTC, currentTime + 20 * DateUtils.MINUTE_IN_MILLIS);
677         ntm.expectTestNotification(2, id3, PRIORITY_HIGH);
678         ntm.expectTestNotification(1, id4, PRIORITY_HIGH);
679         ntm.expectTestNotification(3, id2, PRIORITY_DEFAULT);
680         ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id1, PRIORITY_MIN);
681         AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
682                 currentTime, AlertService.MAX_NOTIFICATIONS);
683         ntm.validateNotificationsAndReset();
684 
685         // Test at 10:14am - next refresh should be 1 min later (15 min into event 'id4').
686         currentTime = Utils.createTimeInMillis(0, 14, 10, day, month, year,
687                 Time.getCurrentTimezone());
688         alarmMgr.expectAlarmTime(AlarmManager.RTC, currentTime + 1 * DateUtils.MINUTE_IN_MILLIS);
689         ntm.expectTestNotification(1, id4, PRIORITY_HIGH);
690         ntm.expectTestNotification(2, id2, PRIORITY_DEFAULT);
691         ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, new int[] {id3, id1},
692                 PRIORITY_MIN);
693         AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
694                 currentTime, AlertService.MAX_NOTIFICATIONS);
695         ntm.validateNotificationsAndReset();
696 
697         // Test at 10:15am - next refresh should be tomorrow midnight (end of all day event 'id2').
698         currentTime = Utils.createTimeInMillis(0, 15, 10, day, month, year,
699                 Time.getCurrentTimezone());
700         alarmMgr.expectAlarmTime(AlarmManager.RTC, Utils.createTimeInMillis(0, 0, 23,
701                 tomorrow.monthDay, tomorrow.month, tomorrow.year, Time.getCurrentTimezone()));
702         ntm.expectTestNotification(1, id2, PRIORITY_DEFAULT);
703         ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
704                 new int[] {id4, id3, id1}, PRIORITY_MIN);
705         AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
706                 currentTime, AlertService.MAX_NOTIFICATIONS);
707         ntm.validateNotificationsAndReset();
708     }
709 
createNotificationInfo(long eventId)710     private NotificationInfo createNotificationInfo(long eventId) {
711         return new NotificationInfo("eventName", "location", "description", 100L, 200L, eventId,
712                 false, false);
713     }
714 
createTimeInMillis(int hour, int minute)715     private static long createTimeInMillis(int hour, int minute) {
716         return Utils.createTimeInMillis(0 /* second */, minute, hour, 1 /* day */, 1 /* month */,
717                 2012 /* year */, Time.getCurrentTimezone());
718     }
719 
720     @SmallTest
testProcessQuery_skipDeclinedDismissed()721     public void testProcessQuery_skipDeclinedDismissed() {
722         int declinedEventId = 1;
723         int dismissedEventId = 2;
724         int acceptedEventId = 3;
725         long acceptedStartTime = createTimeInMillis(10, 0);
726         long acceptedEndTime = createTimeInMillis(10, 30);
727 
728         AlertsTable at = new AlertsTable();
729         at.addAlertRow(declinedEventId, SCHEDULED, DECLINED, 0, createTimeInMillis(9, 0),
730                 createTimeInMillis(10, 0), 0);
731         at.addAlertRow(dismissedEventId, SCHEDULED, DISMISSED, 0, createTimeInMillis(9, 30),
732                 createTimeInMillis(11, 0), 0);
733         at.addAlertRow(acceptedEventId, SCHEDULED, ACCEPTED, 1, acceptedStartTime, acceptedEndTime,
734                 0);
735 
736         ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
737         ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
738         ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
739         long currentTime = createTimeInMillis(5, 0);
740         AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
741                 mediumPriority, lowPriority);
742 
743         assertEquals(0, lowPriority.size());
744         assertEquals(0, mediumPriority.size());
745         assertEquals(1, highPriority.size());
746         assertEquals(acceptedEventId, highPriority.get(0).eventId);
747         assertEquals(acceptedStartTime, highPriority.get(0).startMillis);
748         assertEquals(acceptedEndTime, highPriority.get(0).endMillis);
749         assertTrue(highPriority.get(0).allDay);
750     }
751 
752     @SmallTest
testProcessQuery_newAlert()753     public void testProcessQuery_newAlert() {
754         int scheduledAlertEventId = 1;
755         int firedAlertEventId = 2;
756 
757         AlertsTable at = new AlertsTable();
758         at.addAlertRow(scheduledAlertEventId, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
759                 createTimeInMillis(10, 0), 0);
760         at.addAlertRow(firedAlertEventId, FIRED, ACCEPTED, 0, createTimeInMillis(4, 0),
761                 createTimeInMillis(10, 30), 0);
762 
763         ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
764         ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
765         ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
766         long currentTime = createTimeInMillis(5, 0);
767         AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
768                 mediumPriority, lowPriority);
769 
770         assertEquals(0, lowPriority.size());
771         assertEquals(0, mediumPriority.size());
772         assertEquals(2, highPriority.size());
773         assertEquals(scheduledAlertEventId, highPriority.get(0).eventId);
774         assertTrue("newAlert should be ON for scheduled alerts", highPriority.get(0).newAlert);
775         assertEquals(firedAlertEventId, highPriority.get(1).eventId);
776         assertFalse("newAlert should be OFF for fired alerts", highPriority.get(1).newAlert);
777     }
778 
779     @SmallTest
testProcessQuery_recurringEvent()780     public void testProcessQuery_recurringEvent() {
781         int eventId = 1;
782         long earlierStartTime = createTimeInMillis(10, 0);
783         long laterStartTime = createTimeInMillis(11, 0);
784 
785         ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
786         ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
787         ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
788 
789         AlertsTable at = new AlertsTable();
790         at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 0, laterStartTime,
791                 laterStartTime + DateUtils.HOUR_IN_MILLIS, 0);
792         at.addAlertRow(eventId, FIRED, ACCEPTED, 0, earlierStartTime,
793                 earlierStartTime + DateUtils.HOUR_IN_MILLIS, 0);
794 
795         // Both events in the future: the earliest one should be chosen.
796         long currentTime = earlierStartTime - DateUtils.DAY_IN_MILLIS * 5;
797         AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
798                 mediumPriority, lowPriority);
799         assertEquals(0, lowPriority.size());
800         assertEquals(0, mediumPriority.size());
801         assertEquals(1, highPriority.size());
802         assertEquals("Recurring event with earlier start time expected", earlierStartTime,
803                 highPriority.get(0).startMillis);
804 
805         // Increment time just past the earlier event: the earlier one should be chosen.
806         highPriority.clear();
807         currentTime = earlierStartTime + DateUtils.MINUTE_IN_MILLIS * 10;
808         AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
809                 mediumPriority, lowPriority);
810         assertEquals(0, lowPriority.size());
811         assertEquals(0, mediumPriority.size());
812         assertEquals(1, highPriority.size());
813         assertEquals("Recurring event with earlier start time expected", earlierStartTime,
814                 highPriority.get(0).startMillis);
815 
816         // Increment time to 15 min past the earlier event: the later one should be chosen.
817         highPriority.clear();
818         currentTime = earlierStartTime + DateUtils.MINUTE_IN_MILLIS * 15;
819         AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
820                 mediumPriority, lowPriority);
821         assertEquals(0, lowPriority.size());
822         assertEquals(0, mediumPriority.size());
823         assertEquals(1, highPriority.size());
824         assertEquals("Recurring event with later start time expected", laterStartTime,
825                 highPriority.get(0).startMillis);
826 
827         // Both events in the past: the later one should be chosen (in the low priority bucket).
828         highPriority.clear();
829         currentTime = laterStartTime + DateUtils.DAY_IN_MILLIS * 5;
830         AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
831                 mediumPriority, lowPriority);
832         assertEquals(0, highPriority.size());
833         assertEquals(0, mediumPriority.size());
834         assertEquals(1, lowPriority.size());
835         assertEquals("Recurring event with later start time expected", laterStartTime,
836                 lowPriority.get(0).startMillis);
837     }
838 
839     @SmallTest
testProcessQuery_recurringAllDayEvent()840     public void testProcessQuery_recurringAllDayEvent() {
841         int eventId = 1;
842         long day1 = Utils.createTimeInMillis(0, 0, 0, 1, 5, 2012, Time.TIMEZONE_UTC);
843         long day2 = Utils.createTimeInMillis(0, 0, 0, 2, 5, 2012, Time.TIMEZONE_UTC);
844 
845         ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
846         ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
847         ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
848 
849         AlertsTable at = new AlertsTable();
850         at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 1, day2, day2 + DateUtils.HOUR_IN_MILLIS * 24,
851                 0);
852         at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 1, day1, day1 + DateUtils.HOUR_IN_MILLIS * 24,
853                 0);
854 
855         // Both events in the future: the earliest one should be chosen.
856         long currentTime = day1 - DateUtils.DAY_IN_MILLIS * 3;
857         AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
858                 mediumPriority, lowPriority);
859         assertEquals(0, lowPriority.size());
860         assertEquals(0, mediumPriority.size());
861         assertEquals(1, highPriority.size());
862         assertEquals("Recurring event with earlier start time expected", day1,
863                 highPriority.get(0).startMillis);
864 
865         // Increment time just past the earlier event (to 12:10am).  The earlier one should
866         // be chosen.
867         highPriority.clear();
868         currentTime = Utils.createTimeInMillis(0, 10, 0, 1, 5, 2012, Time.getCurrentTimezone());
869         AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
870                 mediumPriority, lowPriority);
871         assertEquals(0, lowPriority.size());
872         assertEquals(0, mediumPriority.size());
873         assertEquals(1, highPriority.size());
874         assertEquals("Recurring event with earlier start time expected", day1,
875                 highPriority.get(0).startMillis);
876 
877         // Increment time to 15 min past the earlier event: the later one should be chosen.
878         highPriority.clear();
879         currentTime = Utils.createTimeInMillis(0, 15, 0, 1, 5, 2012, Time.getCurrentTimezone());
880         AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
881                 mediumPriority, lowPriority);
882         assertEquals(0, lowPriority.size());
883         assertEquals(0, mediumPriority.size());
884         assertEquals(1, highPriority.size());
885         assertEquals("Recurring event with earlier start time expected", day2,
886                 highPriority.get(0).startMillis);
887 
888         // Both events in the past: the later one should be chosen (in the low priority bucket).
889         highPriority.clear();
890         currentTime = day2 + DateUtils.DAY_IN_MILLIS * 1;
891         AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
892                 mediumPriority, lowPriority);
893         assertEquals(0, highPriority.size());
894         assertEquals(0, mediumPriority.size());
895         assertEquals(1, lowPriority.size());
896         assertEquals("Recurring event with later start time expected", day2,
897                 lowPriority.get(0).startMillis);
898     }
899 
900     @SmallTest
testRedistributeBuckets_withinLimits()901     public void testRedistributeBuckets_withinLimits() throws Exception {
902         int maxNotifications = 3;
903         ArrayList<NotificationInfo> threeItemList = new ArrayList<NotificationInfo>();
904         threeItemList.add(createNotificationInfo(5));
905         threeItemList.add(createNotificationInfo(4));
906         threeItemList.add(createNotificationInfo(3));
907 
908         // Test when max notifications at high priority.
909         ArrayList<NotificationInfo> high = threeItemList;
910         ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
911         ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
912         AlertService.redistributeBuckets(high, medium, low, maxNotifications);
913         assertEquals(3, high.size());
914         assertEquals(0, medium.size());
915         assertEquals(0, low.size());
916 
917         // Test when max notifications at medium priority.
918         high = new ArrayList<NotificationInfo>();
919         medium = threeItemList;
920         low = new ArrayList<NotificationInfo>();
921         AlertService.redistributeBuckets(high, medium, low, maxNotifications);
922         assertEquals(0, high.size());
923         assertEquals(3, medium.size());
924         assertEquals(0, low.size());
925 
926         // Test when max notifications at high and medium priority
927         high = new ArrayList<NotificationInfo>(threeItemList);
928         medium = new ArrayList<NotificationInfo>();
929         medium.add(high.remove(1));
930         low = new ArrayList<NotificationInfo>();
931         AlertService.redistributeBuckets(high, medium, low, maxNotifications);
932         assertEquals(2, high.size());
933         assertEquals(1, medium.size());
934         assertEquals(0, low.size());
935     }
936 
937     @SmallTest
testRedistributeBuckets_tooManyHighPriority()938     public void testRedistributeBuckets_tooManyHighPriority() throws Exception {
939         ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>();
940         ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
941         ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
942         high.add(createNotificationInfo(5));
943         high.add(createNotificationInfo(4));
944         high.add(createNotificationInfo(3));
945         high.add(createNotificationInfo(2));
946         high.add(createNotificationInfo(1));
947 
948         // Invoke the method under test.
949         int maxNotifications = 3;
950         AlertService.redistributeBuckets(high, medium, low, maxNotifications);
951 
952         // Verify some high priority were kicked out.
953         assertEquals(3, high.size());
954         assertEquals(3, high.get(0).eventId);
955         assertEquals(2, high.get(1).eventId);
956         assertEquals(1, high.get(2).eventId);
957 
958         // Verify medium priority untouched.
959         assertEquals(0, medium.size());
960 
961         // Verify the extras went to low priority.
962         assertEquals(2, low.size());
963         assertEquals(5, low.get(0).eventId);
964         assertEquals(4, low.get(1).eventId);
965     }
966 
967     @SmallTest
testRedistributeBuckets_tooManyMediumPriority()968     public void testRedistributeBuckets_tooManyMediumPriority() throws Exception {
969         ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>();
970         ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
971         ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
972         high.add(createNotificationInfo(5));
973         high.add(createNotificationInfo(4));
974         medium.add(createNotificationInfo(3));
975         medium.add(createNotificationInfo(2));
976         medium.add(createNotificationInfo(1));
977 
978         // Invoke the method under test.
979         int maxNotifications = 3;
980         AlertService.redistributeBuckets(high, medium, low, maxNotifications);
981 
982         // Verify high priority untouched.
983         assertEquals(2, high.size());
984         assertEquals(5, high.get(0).eventId);
985         assertEquals(4, high.get(1).eventId);
986 
987         // Verify some medium priority were kicked out (the ones near the end of the
988         // list).
989         assertEquals(1, medium.size());
990         assertEquals(3, medium.get(0).eventId);
991 
992         // Verify the extras went to low priority.
993         assertEquals(2, low.size());
994         assertEquals(2, low.get(0).eventId);
995         assertEquals(1, low.get(1).eventId);
996     }
997 
998     @SmallTest
testRedistributeBuckets_tooManyHighMediumPriority()999     public void testRedistributeBuckets_tooManyHighMediumPriority() throws Exception {
1000         ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>();
1001         ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
1002         ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
1003         high.add(createNotificationInfo(8));
1004         high.add(createNotificationInfo(7));
1005         high.add(createNotificationInfo(6));
1006         high.add(createNotificationInfo(5));
1007         high.add(createNotificationInfo(4));
1008         medium.add(createNotificationInfo(3));
1009         medium.add(createNotificationInfo(2));
1010         medium.add(createNotificationInfo(1));
1011 
1012         // Invoke the method under test.
1013         int maxNotifications = 3;
1014         AlertService.redistributeBuckets(high, medium, low, maxNotifications);
1015 
1016         // Verify high priority.
1017         assertEquals(3, high.size());
1018         assertEquals(6, high.get(0).eventId);
1019         assertEquals(5, high.get(1).eventId);
1020         assertEquals(4, high.get(2).eventId);
1021 
1022         // Verify some medium priority.
1023         assertEquals(0, medium.size());
1024 
1025         // Verify low priority.
1026         assertEquals(5, low.size());
1027         assertEquals(8, low.get(0).eventId);
1028         assertEquals(7, low.get(1).eventId);
1029         assertEquals(3, low.get(2).eventId);
1030         assertEquals(2, low.get(3).eventId);
1031         assertEquals(1, low.get(4).eventId);
1032     }
1033 }
1034