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