AlertServiceTest.java revision bce26a2a19499790595e48ca36b43eb130e4a3fb
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.content.SharedPreferences;
24import android.database.MatrixCursor;
25import android.provider.CalendarContract.Attendees;
26import android.provider.CalendarContract.CalendarAlerts;
27import android.test.AndroidTestCase;
28import android.test.suitebuilder.annotation.SmallTest;
29import android.test.suitebuilder.annotation.Smoke;
30import android.text.format.DateUtils;
31import android.text.format.Time;
32
33import com.android.calendar.GeneralPreferences;
34import com.android.calendar.alerts.AlertService.NotificationInfo;
35import com.android.calendar.alerts.AlertService.NotificationWrapper;
36
37import java.util.ArrayList;
38import java.util.Arrays;
39import java.util.Map;
40import java.util.Set;
41
42public class AlertServiceTest extends AndroidTestCase {
43
44    class MockSharedPreferences implements SharedPreferences {
45
46        /* "always", "silent", depends on ringer mode */
47        private String mVibrateWhen = "always";
48        private String mRingtone = "/some/cool/ringtone";
49        private boolean mPopup = true;
50
51        @Override
52        public boolean contains(String key) {
53            if (GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN.equals(key)) {
54                return true;
55            }
56            return false;
57        }
58
59        @Override
60        public boolean getBoolean(String key, boolean defValue) {
61            if (GeneralPreferences.KEY_ALERTS_POPUP.equals(key)) {
62                return mPopup;
63            }
64            throw new IllegalArgumentException();
65        }
66
67        @Override
68        public String getString(String key, String defValue) {
69            if (GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN.equals(key)) {
70                return mVibrateWhen;
71            }
72            if (GeneralPreferences.KEY_ALERTS_RINGTONE.equals(key)) {
73                return mRingtone;
74            }
75            throw new IllegalArgumentException();
76        }
77
78        @Override
79        public Map<String, ?> getAll() {
80            throw new IllegalArgumentException();
81        }
82
83        @Override
84        public Set<String> getStringSet(String key, Set<String> defValues) {
85            throw new IllegalArgumentException();
86        }
87
88        @Override
89        public int getInt(String key, int defValue) {
90            throw new IllegalArgumentException();
91        }
92
93        @Override
94        public long getLong(String key, long defValue) {
95            throw new IllegalArgumentException();
96        }
97
98        @Override
99        public float getFloat(String key, float defValue) {
100            throw new IllegalArgumentException();
101        }
102
103        @Override
104        public Editor edit() {
105            throw new IllegalArgumentException();
106        }
107
108        @Override
109        public void registerOnSharedPreferenceChangeListener(
110                OnSharedPreferenceChangeListener listener) {
111            throw new IllegalArgumentException();
112        }
113
114        @Override
115        public void unregisterOnSharedPreferenceChangeListener(
116                OnSharedPreferenceChangeListener listener) {
117            throw new IllegalArgumentException();
118        }
119
120    }
121
122    // Created these constants so the test cases are shorter
123    public static final int SCHEDULED = CalendarAlerts.STATE_SCHEDULED;
124    public static final int FIRED = CalendarAlerts.STATE_FIRED;
125    public static final int DISMISSED = CalendarAlerts.STATE_DISMISSED;
126
127    public static final int ACCEPTED = Attendees.ATTENDEE_STATUS_ACCEPTED;
128    public static final int DECLINED = Attendees.ATTENDEE_STATUS_DECLINED;
129    public static final int INVITED = Attendees.ATTENDEE_STATUS_INVITED;
130    public static final int TENTATIVE = Attendees.ATTENDEE_STATUS_TENTATIVE;
131
132    class NotificationInstance {
133        int mAlertId;
134        int[] mAlertIdsInDigest;
135        int mPriority;
136
137        public NotificationInstance(int alertId, int priority) {
138            mAlertId = alertId;
139            mPriority = priority;
140        }
141
142        public NotificationInstance(int[] alertIdsInDigest, int priority) {
143            mAlertIdsInDigest = alertIdsInDigest;
144            mPriority = priority;
145        }
146    }
147
148    class Alert {
149        long mEventId;
150        int mAlertStatus;
151        int mResponseStatus;
152        int mAllDay;
153        long mBegin;
154        long mEnd;
155        int mMinute;
156        long mAlarmTime;
157
158        public Alert(long eventId, int alertStatus, int responseStatus, int allDay, long begin,
159                long end, int minute, long alarmTime) {
160            mEventId = eventId;
161            mAlertStatus = alertStatus;
162            mResponseStatus = responseStatus;
163            mAllDay = allDay;
164            mBegin = begin;
165            mEnd = end;
166            mMinute = minute;
167            mAlarmTime = alarmTime;
168        }
169
170    }
171
172    class AlertsTable {
173
174        ArrayList<Alert> mAlerts = new ArrayList<Alert>();
175
176        int addAlertRow(long eventId, int alertStatus, int responseStatus, int allDay, long begin,
177                long end, long alarmTime) {
178            Alert a = new Alert(eventId, alertStatus, responseStatus, allDay, begin, end,
179                    5 /* minute */, alarmTime);
180            int id = mAlerts.size();
181            mAlerts.add(a);
182            return id;
183        }
184
185        public MatrixCursor getAlertCursor() {
186            MatrixCursor alertCursor = new MatrixCursor(AlertService.ALERT_PROJECTION);
187
188            int i = 0;
189            for (Alert a : mAlerts) {
190                Object[] ca = {
191                        i++,
192                        a.mEventId,
193                        a.mAlertStatus,
194                        "Title" + a.mEventId + " " + a.mMinute,
195                        "Loc" + a.mEventId,
196                        a.mResponseStatus,
197                        a.mAllDay,
198                        a.mAlarmTime > 0 ? a.mAlarmTime : a.mBegin - a.mMinute * 60 * 1000,
199                        a.mMinute,
200                        a.mBegin,
201                        a.mEnd,
202                        "Desc: " + a.mAlarmTime
203                };
204                alertCursor.addRow(ca);
205            }
206            return alertCursor;
207        }
208
209    }
210
211    class NotificationTestManager implements NotificationMgr {
212        // Expected notifications
213        NotificationInstance[] mNotifications;
214
215        // Flag to know which notification has been posted or canceled
216        boolean[] mDone;
217
218        // CalendarAlerts table
219        private ArrayList<Alert> mAlerts;
220
221        public NotificationTestManager(ArrayList<Alert> alerts, int maxNotifications) {
222            assertEquals(0, AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID);
223            mAlerts = alerts;
224            mNotifications = new NotificationInstance[maxNotifications + 1];
225        }
226
227        public void expectTestNotification(int notificationId, int alertId, int highPriority) {
228            mNotifications[notificationId] = new NotificationInstance(alertId, highPriority);
229        }
230
231        public void expectTestNotification(int notificationId, int[] alertIds, int priority) {
232            mNotifications[notificationId] = new NotificationInstance(alertIds, priority);
233        }
234
235        private void verifyNotification(int id, NotificationWrapper nw) {
236                assertEquals(mNotifications[id].mPriority, nw.mNotification.priority);
237
238                NotificationInstance expected = mNotifications[id];
239                if (expected.mAlertIdsInDigest == null) {
240                    Alert a = mAlerts.get(expected.mAlertId);
241                    assertEquals("Event ID", a.mEventId, nw.mEventId);
242                    assertEquals("Begin time", a.mBegin, nw.mBegin);
243                    assertEquals("End time", a.mEnd, nw.mEnd);
244                } else {
245                    // Notification should be a digest.
246                    assertNotNull("Posted notification not a digest as expected.", nw.mNw);
247                    assertEquals("Number of notifications in digest not as expected.",
248                            expected.mAlertIdsInDigest.length, nw.mNw.size());
249                    for (int i = 0; i < nw.mNw.size(); i++) {
250                        Alert a = mAlerts.get(expected.mAlertIdsInDigest[i]);
251                        assertEquals("Digest item " + i + ": Event ID not as expected",
252                                a.mEventId, nw.mNw.get(i).mEventId);
253                        assertEquals("Digest item " + i + ": Begin time in digest",
254                                a.mBegin, nw.mNw.get(i).mBegin);
255                        assertEquals("Digest item " + i + ": End time in digest",
256                                a.mEnd, nw.mNw.get(i).mEnd);
257                    }
258                }
259        }
260
261        public void validateNotificationsAndReset() {
262            for (int i = 0; i < mDone.length; i++) {
263                assertTrue("Notification id " + i + " has not been posted", mDone[i]);
264            }
265            Arrays.fill(mDone, false);
266            Arrays.fill(mNotifications, null);
267        }
268
269        ///////////////////////////////
270        // NotificationMgr methods
271        @Override
272        public void cancel(int id) {
273            if (mDone == null) {
274                mDone = new boolean[mNotifications.length];
275            }
276            assertTrue("id out of bound: " + id, 0 <= id);
277            assertTrue("id out of bound: " + id, id < mDone.length);
278            assertFalse("id already used", mDone[id]);
279            mDone[id] = true;
280            assertNull("Unexpected cancel for id " + id, mNotifications[id]);
281        }
282
283        @Override
284        public void cancel(String tag, int id) {
285            throw new IllegalArgumentException();
286        }
287
288        @Override
289        public void cancelAll() {
290            for (int i = 0; i < mNotifications.length; i++) {
291                assertNull("Expecting notification id " + i + ". Got cancelAll", mNotifications[i]);
292
293                if (mDone != null) {
294                    assertFalse("Notification id " + i + " is done but got cancelAll", mDone[i]);
295                }
296            }
297
298            assertNull(mDone); // this should have been null since nothing
299                               // should have been posted
300            mDone = new boolean[mNotifications.length];
301            Arrays.fill(mDone, true);
302        }
303
304        @Override
305        public void notify(int id, NotificationWrapper nw) {
306            if (mDone == null) {
307                mDone = new boolean[mNotifications.length];
308            }
309            assertTrue("id out of bound: " + id, 0 <= id);
310            assertTrue("id out of bound: " + id, id < mDone.length);
311            assertFalse("id already used", mDone[id]);
312            mDone[id] = true;
313
314            assertNotNull("Unexpected notify for id " + id, mNotifications[id]);
315
316            verifyNotification(id, nw);
317        }
318
319        @Override
320        public void notify(String tag, int id, NotificationWrapper nw) {
321            throw new IllegalArgumentException();
322        }
323    }
324
325    // TODO
326    // Catch updates of new state, notify time, and received time
327    // Test ringer, vibrate,
328    // Test digest notifications
329    // Test intents, action email
330    // Catch alarmmgr calls
331
332    @Smoke
333    @SmallTest
334    public void testNoAlerts() {
335        MockSharedPreferences prefs = new MockSharedPreferences();
336        AlertsTable at = new AlertsTable();
337        NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
338                AlertService.MAX_NOTIFICATIONS);
339
340        // Test no alert
341        long currentTime = 1000000;
342        AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime,
343                AlertService.MAX_NOTIFICATIONS);
344        ntm.validateNotificationsAndReset();
345    }
346
347    @Smoke
348    @SmallTest
349    public void testSingleAlert() {
350        MockSharedPreferences prefs = new MockSharedPreferences();
351        AlertsTable at = new AlertsTable();
352        NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
353                AlertService.MAX_NOTIFICATIONS);
354
355        int id = at.addAlertRow(100, SCHEDULED, ACCEPTED, 0 /* all day */, 1300000, 2300000, 0);
356
357        // Test one up coming alert
358        long currentTime = 1000000;
359        ntm.expectTestNotification(1, id, PRIORITY_HIGH);
360
361        AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime,
362                AlertService.MAX_NOTIFICATIONS);
363        ntm.validateNotificationsAndReset(); // This wipes out notification
364                                             // tests added so far
365
366        // Test half way into an event
367        currentTime = 2300000;
368        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id, PRIORITY_DEFAULT);
369
370        AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime,
371                AlertService.MAX_NOTIFICATIONS);
372        ntm.validateNotificationsAndReset();
373
374        // Test event ended
375        currentTime = 4300000;
376        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id, PRIORITY_MIN);
377
378        AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime,
379                AlertService.MAX_NOTIFICATIONS);
380        ntm.validateNotificationsAndReset();
381    }
382
383    @SmallTest
384    public void testMultipleAlerts() {
385        int maxNotifications = 10;
386        MockSharedPreferences prefs = new MockSharedPreferences();
387        AlertsTable at = new AlertsTable();
388        NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications);
389
390        // Current time - 5:00
391        long currentTime = createTimeInMillis(5, 0);
392
393        // Set up future alerts.  The real query implementation sorts by descending start
394        // time so simulate that here with our order of adds to AlertsTable.
395        int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
396                createTimeInMillis(10, 0), 0);
397        int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0),
398                createTimeInMillis(9, 0), 0);
399        int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0),
400                createTimeInMillis(8, 0), 0);
401
402        // Set up concurrent alerts (that started recently).
403        int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0),
404                createTimeInMillis(5, 40), 0);
405        int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55),
406                createTimeInMillis(7, 30), 0);
407        int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50),
408                createTimeInMillis(4, 50), 0);
409
410        // Set up past alerts.
411        int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0),
412                createTimeInMillis(4, 0), 0);
413        int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0),
414                createTimeInMillis(3, 0), 0);
415        int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0),
416                createTimeInMillis(2, 0), 0);
417
418        // Check posted notifications.  The order listed here is the order simulates the
419        // order in the real notification bar (last one posted appears on top), so these
420        // should be lowest start time on top.
421        ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent
422        ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent
423        ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent
424        ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future
425        ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future
426        ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
427        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
428                new int[] {id3, id2, id1}, PRIORITY_MIN);
429        AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime,
430                maxNotifications);
431        ntm.validateNotificationsAndReset();
432
433        // Increase time by 15 minutes to check that some concurrent events dropped
434        // to the low priority bucket.
435        currentTime = createTimeInMillis(5, 15);
436        ntm.expectTestNotification(4, id5, PRIORITY_HIGH); // concurrent
437        ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future
438        ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future
439        ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
440        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
441                new int[] {id6, id4, id3, id2, id1}, PRIORITY_MIN);
442        AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime,
443                maxNotifications);
444        ntm.validateNotificationsAndReset();
445
446        // Increase time so some of the previously future ones change state.
447        currentTime = createTimeInMillis(8, 15);
448        ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
449        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
450                new int[] {id8, id7, id6, id5, id4, id3, id2, id1}, PRIORITY_MIN);
451        AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime,
452                maxNotifications);
453        ntm.validateNotificationsAndReset();
454    }
455
456    @SmallTest
457    public void testMultipleAlerts_max() {
458        MockSharedPreferences prefs = new MockSharedPreferences();
459        AlertsTable at = new AlertsTable();
460
461        // Current time - 5:00
462        long currentTime = createTimeInMillis(5, 0);
463
464        // Set up future alerts.  The real query implementation sorts by descending start
465        // time so simulate that here with our order of adds to AlertsTable.
466        int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
467                createTimeInMillis(10, 0), 0);
468        int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0),
469                createTimeInMillis(9, 0), 0);
470        int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0),
471                createTimeInMillis(8, 0), 0);
472
473        // Set up concurrent alerts (that started recently).
474        int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0),
475                createTimeInMillis(5, 40), 0);
476        int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55),
477                createTimeInMillis(7, 30), 0);
478        int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50),
479                createTimeInMillis(4, 50), 0);
480
481        // Set up past alerts.
482        int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0),
483                createTimeInMillis(4, 0), 0);
484        int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0),
485                createTimeInMillis(3, 0), 0);
486        int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0),
487                createTimeInMillis(2, 0), 0);
488
489        // Test when # alerts = max.
490        int maxNotifications = 6;
491        NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications);
492        ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent
493        ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent
494        ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent
495        ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future
496        ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future
497        ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
498        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
499                new int[] {id3, id2, id1}, PRIORITY_MIN);
500        AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime,
501                maxNotifications);
502        ntm.validateNotificationsAndReset();
503
504        // Test when # alerts > max.
505        maxNotifications = 4;
506        ntm = new NotificationTestManager(at.mAlerts, maxNotifications);
507        ntm.expectTestNotification(4, id4, PRIORITY_HIGH); // concurrent
508        ntm.expectTestNotification(3, id5, PRIORITY_HIGH); // concurrent
509        ntm.expectTestNotification(2, id6, PRIORITY_HIGH); // concurrent
510        ntm.expectTestNotification(1, id7, PRIORITY_HIGH); // future
511        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
512                new int[] {id9, id8, id3, id2, id1}, PRIORITY_MIN);
513        AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime,
514                maxNotifications);
515        ntm.validateNotificationsAndReset();
516    }
517
518    private NotificationInfo createNotificationInfo(long eventId) {
519        return new NotificationInfo("eventName", "location", "description", 100L, 200L, eventId,
520                false, false);
521    }
522
523    private static long createTimeInMillis(int hour, int minute) {
524        return createTimeInMillis(0 /* second */, minute, hour, 1 /* day */, 1 /* month */,
525                2012 /* year */, Time.getCurrentTimezone());
526    }
527
528    private static long createTimeInMillis(int second, int minute, int hour, int monthDay,
529            int month, int year, String timezone) {
530        Time t = new Time(timezone);
531        t.set(second, minute, hour, monthDay, month, year);
532        t.normalize(false);
533        return t.toMillis(false);
534    }
535
536    @SmallTest
537    public void testProcessQuery_skipDeclinedDismissed() {
538        int declinedEventId = 1;
539        int dismissedEventId = 2;
540        int acceptedEventId = 3;
541        long acceptedStartTime = createTimeInMillis(10, 0);
542        long acceptedEndTime = createTimeInMillis(10, 30);
543
544        AlertsTable at = new AlertsTable();
545        at.addAlertRow(declinedEventId, SCHEDULED, DECLINED, 0, createTimeInMillis(9, 0),
546                createTimeInMillis(10, 0), 0);
547        at.addAlertRow(dismissedEventId, SCHEDULED, DISMISSED, 0, createTimeInMillis(9, 30),
548                createTimeInMillis(11, 0), 0);
549        at.addAlertRow(acceptedEventId, SCHEDULED, ACCEPTED, 1, acceptedStartTime, acceptedEndTime,
550                0);
551
552        ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
553        ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
554        ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
555        long currentTime = createTimeInMillis(5, 0);
556        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
557                mediumPriority, lowPriority);
558
559        assertEquals(0, lowPriority.size());
560        assertEquals(0, mediumPriority.size());
561        assertEquals(1, highPriority.size());
562        assertEquals(acceptedEventId, highPriority.get(0).eventId);
563        assertEquals(acceptedStartTime, highPriority.get(0).startMillis);
564        assertEquals(acceptedEndTime, highPriority.get(0).endMillis);
565        assertTrue(highPriority.get(0).allDay);
566    }
567
568    @SmallTest
569    public void testProcessQuery_newAlert() {
570        int scheduledAlertEventId = 1;
571        int firedAlertEventId = 2;
572
573        AlertsTable at = new AlertsTable();
574        at.addAlertRow(scheduledAlertEventId, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
575                createTimeInMillis(10, 0), 0);
576        at.addAlertRow(firedAlertEventId, FIRED, ACCEPTED, 0, createTimeInMillis(10, 0),
577                createTimeInMillis(10, 30), 0);
578
579        ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
580        ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
581        ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
582        long currentTime = createTimeInMillis(5, 0);
583        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
584                mediumPriority, lowPriority);
585
586        assertEquals(0, lowPriority.size());
587        assertEquals(0, mediumPriority.size());
588        assertEquals(2, highPriority.size());
589        assertEquals(scheduledAlertEventId, highPriority.get(0).eventId);
590        assertTrue("newAlert should be ON for scheduled alerts", highPriority.get(0).newAlert);
591        assertEquals(firedAlertEventId, highPriority.get(1).eventId);
592        assertFalse("newAlert should be OFF for fired alerts", highPriority.get(1).newAlert);
593    }
594
595    @SmallTest
596    public void testProcessQuery_recurringEvent() {
597        int eventId = 1;
598        long earlierStartTime = createTimeInMillis(10, 0);
599        long laterStartTime = createTimeInMillis(11, 0);
600
601        ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
602        ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
603        ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
604
605        AlertsTable at = new AlertsTable();
606        at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 0, laterStartTime,
607                laterStartTime + DateUtils.HOUR_IN_MILLIS, 0);
608        at.addAlertRow(eventId, FIRED, ACCEPTED, 0, earlierStartTime,
609                earlierStartTime + DateUtils.HOUR_IN_MILLIS, 0);
610
611        // Both events in the future: the earliest one should be chosen.
612        long currentTime = earlierStartTime - DateUtils.DAY_IN_MILLIS * 5;
613        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
614                mediumPriority, lowPriority);
615        assertEquals(0, lowPriority.size());
616        assertEquals(0, mediumPriority.size());
617        assertEquals(1, highPriority.size());
618        assertEquals("Recurring event with earlier start time expected", earlierStartTime,
619                highPriority.get(0).startMillis);
620
621        // Increment time just past the earlier event: the earlier one should be chosen.
622        highPriority.clear();
623        currentTime = earlierStartTime + DateUtils.MINUTE_IN_MILLIS * 10;
624        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
625                mediumPriority, lowPriority);
626        assertEquals(0, lowPriority.size());
627        assertEquals(0, mediumPriority.size());
628        assertEquals(1, highPriority.size());
629        assertEquals("Recurring event with earlier start time expected", earlierStartTime,
630                highPriority.get(0).startMillis);
631
632        // Increment time to 15 min past the earlier event: the later one should be chosen.
633        highPriority.clear();
634        currentTime = earlierStartTime + DateUtils.MINUTE_IN_MILLIS * 15;
635        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
636                mediumPriority, lowPriority);
637        assertEquals(0, lowPriority.size());
638        assertEquals(0, mediumPriority.size());
639        assertEquals(1, highPriority.size());
640        assertEquals("Recurring event with later start time expected", laterStartTime,
641                highPriority.get(0).startMillis);
642
643        // Both events in the past: the later one should be chosen (in the low priority bucket).
644        highPriority.clear();
645        currentTime = laterStartTime + DateUtils.DAY_IN_MILLIS * 5;
646        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
647                mediumPriority, lowPriority);
648        assertEquals(0, highPriority.size());
649        assertEquals(0, mediumPriority.size());
650        assertEquals(1, lowPriority.size());
651        assertEquals("Recurring event with later start time expected", laterStartTime,
652                lowPriority.get(0).startMillis);
653    }
654
655    @SmallTest
656    public void testProcessQuery_recurringAllDayEvent() {
657        int eventId = 1;
658        long day1 = createTimeInMillis(0, 0, 0, 1, 5, 2012, Time.TIMEZONE_UTC);
659        long day2 = createTimeInMillis(0, 0, 0, 2, 5, 2012, Time.TIMEZONE_UTC);
660
661        ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
662        ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
663        ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
664
665        AlertsTable at = new AlertsTable();
666        at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 1, day2, day2 + DateUtils.HOUR_IN_MILLIS * 24,
667                0);
668        at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 1, day1, day1 + DateUtils.HOUR_IN_MILLIS * 24,
669                0);
670
671        // Both events in the future: the earliest one should be chosen.
672        long currentTime = day1 - DateUtils.DAY_IN_MILLIS * 3;
673        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
674                mediumPriority, lowPriority);
675        assertEquals(0, lowPriority.size());
676        assertEquals(0, mediumPriority.size());
677        assertEquals(1, highPriority.size());
678        assertEquals("Recurring event with earlier start time expected", day1,
679                highPriority.get(0).startMillis);
680
681        // Increment time just past the earlier event (to 12:10am).  The earlier one should
682        // be chosen.
683        highPriority.clear();
684        currentTime = createTimeInMillis(0, 10, 0, 1, 5, 2012, Time.getCurrentTimezone());
685        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
686                mediumPriority, lowPriority);
687        assertEquals(0, lowPriority.size());
688        assertEquals(0, mediumPriority.size());
689        assertEquals(1, highPriority.size());
690        assertEquals("Recurring event with earlier start time expected", day1,
691                highPriority.get(0).startMillis);
692
693        // Increment time to 15 min past the earlier event: the later one should be chosen.
694        highPriority.clear();
695        currentTime = createTimeInMillis(0, 15, 0, 1, 5, 2012, Time.getCurrentTimezone());
696        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
697                mediumPriority, lowPriority);
698        assertEquals(0, lowPriority.size());
699        assertEquals(0, mediumPriority.size());
700        assertEquals(1, highPriority.size());
701        assertEquals("Recurring event with earlier start time expected", day2,
702                highPriority.get(0).startMillis);
703
704        // Both events in the past: the later one should be chosen (in the low priority bucket).
705        highPriority.clear();
706        currentTime = day2 + DateUtils.DAY_IN_MILLIS * 1;
707        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
708                mediumPriority, lowPriority);
709        assertEquals(0, highPriority.size());
710        assertEquals(0, mediumPriority.size());
711        assertEquals(1, lowPriority.size());
712        assertEquals("Recurring event with later start time expected", day2,
713                lowPriority.get(0).startMillis);
714    }
715
716    @SmallTest
717    public void testRedistributeBuckets_withinLimits() throws Exception {
718        int maxNotifications = 3;
719        ArrayList<NotificationInfo> threeItemList = new ArrayList<NotificationInfo>();
720        threeItemList.add(createNotificationInfo(5));
721        threeItemList.add(createNotificationInfo(4));
722        threeItemList.add(createNotificationInfo(3));
723
724        // Test when max notifications at high priority.
725        ArrayList<NotificationInfo> high = threeItemList;
726        ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
727        ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
728        AlertService.redistributeBuckets(high, medium, low, maxNotifications);
729        assertEquals(3, high.size());
730        assertEquals(0, medium.size());
731        assertEquals(0, low.size());
732
733        // Test when max notifications at medium priority.
734        high = new ArrayList<NotificationInfo>();
735        medium = threeItemList;
736        low = new ArrayList<NotificationInfo>();
737        AlertService.redistributeBuckets(high, medium, low, maxNotifications);
738        assertEquals(0, high.size());
739        assertEquals(3, medium.size());
740        assertEquals(0, low.size());
741
742        // Test when max notifications at high and medium priority
743        high = new ArrayList<NotificationInfo>(threeItemList);
744        medium = new ArrayList<NotificationInfo>();
745        medium.add(high.remove(1));
746        low = new ArrayList<NotificationInfo>();
747        AlertService.redistributeBuckets(high, medium, low, maxNotifications);
748        assertEquals(2, high.size());
749        assertEquals(1, medium.size());
750        assertEquals(0, low.size());
751    }
752
753    @SmallTest
754    public void testRedistributeBuckets_tooManyHighPriority() throws Exception {
755        ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>();
756        ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
757        ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
758        high.add(createNotificationInfo(5));
759        high.add(createNotificationInfo(4));
760        high.add(createNotificationInfo(3));
761        high.add(createNotificationInfo(2));
762        high.add(createNotificationInfo(1));
763
764        // Invoke the method under test.
765        int maxNotifications = 3;
766        AlertService.redistributeBuckets(high, medium, low, maxNotifications);
767
768        // Verify some high priority were kicked out.
769        assertEquals(3, high.size());
770        assertEquals(3, high.get(0).eventId);
771        assertEquals(2, high.get(1).eventId);
772        assertEquals(1, high.get(2).eventId);
773
774        // Verify medium priority untouched.
775        assertEquals(0, medium.size());
776
777        // Verify the extras went to low priority.
778        assertEquals(2, low.size());
779        assertEquals(5, low.get(0).eventId);
780        assertEquals(4, low.get(1).eventId);
781    }
782
783    @SmallTest
784    public void testRedistributeBuckets_tooManyMediumPriority() throws Exception {
785        ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>();
786        ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
787        ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
788        high.add(createNotificationInfo(5));
789        high.add(createNotificationInfo(4));
790        medium.add(createNotificationInfo(3));
791        medium.add(createNotificationInfo(2));
792        medium.add(createNotificationInfo(1));
793
794        // Invoke the method under test.
795        int maxNotifications = 3;
796        AlertService.redistributeBuckets(high, medium, low, maxNotifications);
797
798        // Verify high priority untouched.
799        assertEquals(2, high.size());
800        assertEquals(5, high.get(0).eventId);
801        assertEquals(4, high.get(1).eventId);
802
803        // Verify some medium priority were kicked out (the ones near the end of the
804        // list).
805        assertEquals(1, medium.size());
806        assertEquals(3, medium.get(0).eventId);
807
808        // Verify the extras went to low priority.
809        assertEquals(2, low.size());
810        assertEquals(2, low.get(0).eventId);
811        assertEquals(1, low.get(1).eventId);
812    }
813
814    @SmallTest
815    public void testRedistributeBuckets_tooManyHighMediumPriority() throws Exception {
816        ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>();
817        ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
818        ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
819        high.add(createNotificationInfo(8));
820        high.add(createNotificationInfo(7));
821        high.add(createNotificationInfo(6));
822        high.add(createNotificationInfo(5));
823        high.add(createNotificationInfo(4));
824        medium.add(createNotificationInfo(3));
825        medium.add(createNotificationInfo(2));
826        medium.add(createNotificationInfo(1));
827
828        // Invoke the method under test.
829        int maxNotifications = 3;
830        AlertService.redistributeBuckets(high, medium, low, maxNotifications);
831
832        // Verify high priority.
833        assertEquals(3, high.size());
834        assertEquals(6, high.get(0).eventId);
835        assertEquals(5, high.get(1).eventId);
836        assertEquals(4, high.get(2).eventId);
837
838        // Verify some medium priority.
839        assertEquals(0, medium.size());
840
841        // Verify low priority.
842        assertEquals(5, low.size());
843        assertEquals(8, low.get(0).eventId);
844        assertEquals(7, low.get(1).eventId);
845        assertEquals(3, low.get(2).eventId);
846        assertEquals(2, low.get(3).eventId);
847        assertEquals(1, low.get(4).eventId);
848    }
849}
850