AlertServiceTest.java revision 3a07a68da6460c36a5dbec5b8828baa4355dbe04
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    // TODO
389    // Catch updates of new state, notify time, and received time
390    // Test ringer, vibrate,
391    // Test intents, action email
392
393    @Smoke
394    @SmallTest
395    public void testGenerateAlerts_none() {
396        MockSharedPreferences prefs = new MockSharedPreferences();
397        AlertsTable at = new AlertsTable();
398        NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
399                AlertService.MAX_NOTIFICATIONS);
400
401        // Test no alert
402        long currentTime = 1000000;
403        AlertService.generateAlerts(mContext, ntm, new MockAlarmManager(mContext), prefs,
404                at.getAlertCursor(), currentTime, AlertService.MAX_NOTIFICATIONS);
405        ntm.validateNotificationsAndReset();
406    }
407
408    @Smoke
409    @SmallTest
410    public void testGenerateAlerts_single() {
411        MockSharedPreferences prefs = new MockSharedPreferences();
412        MockAlarmManager alarmMgr = new MockAlarmManager(mContext);
413        AlertsTable at = new AlertsTable();
414        NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
415                AlertService.MAX_NOTIFICATIONS);
416
417        int id = at.addAlertRow(100, SCHEDULED, ACCEPTED, 0 /* all day */, 1300000, 2300000, 0);
418
419        // Test one up coming alert
420        long currentTime = 1000000;
421        ntm.expectTestNotification(1, id, PRIORITY_HIGH);
422
423        AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(), currentTime,
424                AlertService.MAX_NOTIFICATIONS);
425        ntm.validateNotificationsAndReset(); // This wipes out notification
426                                             // tests added so far
427
428        // Test half way into an event
429        currentTime = 2300000;
430        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id, PRIORITY_MIN);
431
432        AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(), currentTime,
433                AlertService.MAX_NOTIFICATIONS);
434        ntm.validateNotificationsAndReset();
435
436        // Test event ended
437        currentTime = 4300000;
438        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id, PRIORITY_MIN);
439
440        AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(), currentTime,
441                AlertService.MAX_NOTIFICATIONS);
442        ntm.validateNotificationsAndReset();
443    }
444
445    @SmallTest
446    public void testGenerateAlerts_multiple() {
447        int maxNotifications = 10;
448        MockSharedPreferences prefs = new MockSharedPreferences();
449        MockAlarmManager alarmMgr = new MockAlarmManager(mContext);
450        AlertsTable at = new AlertsTable();
451        NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications);
452
453        // Current time - 5:00
454        long currentTime = createTimeInMillis(5, 0);
455
456        // Set up future alerts.  The real query implementation sorts by descending start
457        // time so simulate that here with our order of adds to AlertsTable.
458        int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
459                createTimeInMillis(10, 0), 0);
460        int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0),
461                createTimeInMillis(9, 0), 0);
462        int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0),
463                createTimeInMillis(8, 0), 0);
464
465        // Set up concurrent alerts (that started recently).
466        int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0),
467                createTimeInMillis(5, 40), 0);
468        int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55),
469                createTimeInMillis(7, 30), 0);
470        int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50),
471                createTimeInMillis(4, 50), 0);
472
473        // Set up past alerts.
474        int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0),
475                createTimeInMillis(4, 0), 0);
476        int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0),
477                createTimeInMillis(3, 0), 0);
478        int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0),
479                createTimeInMillis(2, 0), 0);
480
481        // Check posted notifications.  The order listed here is the order simulates the
482        // order in the real notification bar (last one posted appears on top), so these
483        // should be lowest start time on top.
484        ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent
485        ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent
486        ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent
487        ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future
488        ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future
489        ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
490        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
491                new int[] {id3, id2, id1}, PRIORITY_MIN);
492        AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
493                currentTime, maxNotifications);
494        ntm.validateNotificationsAndReset();
495
496        // Increase time by 15 minutes to check that some concurrent events dropped
497        // to the low priority bucket.
498        currentTime = createTimeInMillis(5, 15);
499        ntm.expectTestNotification(4, id5, PRIORITY_HIGH); // concurrent
500        ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future
501        ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future
502        ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
503        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
504                new int[] {id6, id4, id3, id2, id1}, PRIORITY_MIN);
505        AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
506                currentTime, maxNotifications);
507        ntm.validateNotificationsAndReset();
508
509        // Increase time so some of the previously future ones change state.
510        currentTime = createTimeInMillis(8, 15);
511        ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
512        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
513                new int[] {id8, id7, id6, id5, id4, id3, id2, id1}, PRIORITY_MIN);
514        AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
515                currentTime, maxNotifications);
516        ntm.validateNotificationsAndReset();
517    }
518
519    @SmallTest
520    public void testGenerateAlerts_maxAlerts() {
521        MockSharedPreferences prefs = new MockSharedPreferences();
522        MockAlarmManager alarmMgr = new MockAlarmManager(mContext);
523        AlertsTable at = new AlertsTable();
524
525        // Current time - 5:00
526        long currentTime = createTimeInMillis(5, 0);
527
528        // Set up future alerts.  The real query implementation sorts by descending start
529        // time so simulate that here with our order of adds to AlertsTable.
530        int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
531                createTimeInMillis(10, 0), 0);
532        int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0),
533                createTimeInMillis(9, 0), 0);
534        int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0),
535                createTimeInMillis(8, 0), 0);
536
537        // Set up concurrent alerts (that started recently).
538        int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0),
539                createTimeInMillis(5, 40), 0);
540        int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55),
541                createTimeInMillis(7, 30), 0);
542        int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50),
543                createTimeInMillis(4, 50), 0);
544
545        // Set up past alerts.
546        int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0),
547                createTimeInMillis(4, 0), 0);
548        int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0),
549                createTimeInMillis(3, 0), 0);
550        int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0),
551                createTimeInMillis(2, 0), 0);
552
553        // Test when # alerts = max.
554        int maxNotifications = 6;
555        NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications);
556        ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent
557        ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent
558        ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent
559        ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future
560        ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future
561        ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future
562        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
563                new int[] {id3, id2, id1}, PRIORITY_MIN);
564        AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
565                currentTime, maxNotifications);
566        ntm.validateNotificationsAndReset();
567
568        // Test when # alerts > max.
569        maxNotifications = 4;
570        ntm = new NotificationTestManager(at.mAlerts, maxNotifications);
571        ntm.expectTestNotification(4, id4, PRIORITY_HIGH); // concurrent
572        ntm.expectTestNotification(3, id5, PRIORITY_HIGH); // concurrent
573        ntm.expectTestNotification(2, id6, PRIORITY_HIGH); // concurrent
574        ntm.expectTestNotification(1, id7, PRIORITY_HIGH); // future
575        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
576                new int[] {id9, id8, id3, id2, id1}, PRIORITY_MIN);
577        AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
578                currentTime, maxNotifications);
579        ntm.validateNotificationsAndReset();
580    }
581
582    /**
583     * Test that the SharedPreferences are only fetched once for each setting.
584     */
585    @SmallTest
586    public void testGenerateAlerts_sharedPreferences() {
587        MockSharedPreferences prefs = new MockSharedPreferences(true /* strict mode */);
588        AlertsTable at = new AlertsTable();
589        NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
590                AlertService.MAX_NOTIFICATIONS);
591
592        // Current time - 5:00
593        long currentTime = createTimeInMillis(5, 0);
594
595        // Set up future alerts.  The real query implementation sorts by descending start
596        // time so simulate that here with our order of adds to AlertsTable.
597        at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
598                createTimeInMillis(10, 0), 0);
599        at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0),
600                createTimeInMillis(9, 0), 0);
601        at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0),
602                createTimeInMillis(8, 0), 0);
603
604        // If this does not result in a failure (MockSharedPreferences fails for duplicate
605        // queries), then test passes.
606        AlertService.generateAlerts(mContext, ntm, new MockAlarmManager(mContext), prefs,
607                at.getAlertCursor(), currentTime, AlertService.MAX_NOTIFICATIONS);
608    }
609
610    public void testGenerateAlerts_refreshTime() {
611        AlertsTable at = new AlertsTable();
612        MockSharedPreferences prefs = new MockSharedPreferences();
613        MockAlarmManager alarmMgr = new MockAlarmManager(mContext);
614        NotificationTestManager ntm = new NotificationTestManager(at.mAlerts,
615                AlertService.MAX_NOTIFICATIONS);
616
617        // Since AlertService.processQuery uses DateUtils.isToday instead of checking against
618        // the passed in currentTime (not worth allocating the extra Time objects to do so), use
619        // today's date for this test.
620        Time now = new Time();
621        now.setToNow();
622        int day = now.monthDay;
623        int month = now.month;
624        int year = now.year;
625        Time yesterday = new Time();
626        yesterday.set(System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS);
627        Time tomorrow = new Time();
628        tomorrow.set(System.currentTimeMillis() + DateUtils.DAY_IN_MILLIS);
629        long allDayStart = Utils.createTimeInMillis(0, 0, 0, day, month, year, Time.TIMEZONE_UTC);
630
631        /* today 10am - 10:30am */
632        int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0,
633                Utils.createTimeInMillis(0, 0, 10, day, month, year, Time.getCurrentTimezone()),
634                Utils.createTimeInMillis(0, 30, 10, day, month, year, Time.getCurrentTimezone()),
635                        0);
636        /* today 6am - 6am (0 duration event) */
637        int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0,
638                Utils.createTimeInMillis(0, 0, 6, day, month, year, Time.getCurrentTimezone()),
639                Utils.createTimeInMillis(0, 0, 6, day, month, year, Time.getCurrentTimezone()), 0);
640        /* today allDay */
641        int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 1, allDayStart,
642                allDayStart + DateUtils.HOUR_IN_MILLIS * 24, 0);
643        /* yesterday 11pm - today 7am (multiday event) */
644        int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0,
645                Utils.createTimeInMillis(0, 0, 23, yesterday.monthDay, yesterday.month,
646                        yesterday.year, Time.getCurrentTimezone()),
647                Utils.createTimeInMillis(0, 0, 7, day, month, year, Time.getCurrentTimezone()), 0);
648
649        // Test at midnight - next refresh should be 15 min later (15 min into the all
650        // day event).
651        long currentTime = Utils.createTimeInMillis(0, 0, 0, day, month, year,
652                Time.getCurrentTimezone());
653        alarmMgr.expectAlarmTime(AlarmManager.RTC, currentTime + 15 * DateUtils.MINUTE_IN_MILLIS);
654        ntm.expectTestNotification(4, id1, PRIORITY_HIGH);
655        ntm.expectTestNotification(3, id2, PRIORITY_HIGH);
656        ntm.expectTestNotification(2, id3, PRIORITY_HIGH);
657        ntm.expectTestNotification(1, id4, PRIORITY_HIGH);
658        AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
659                currentTime, AlertService.MAX_NOTIFICATIONS);
660        ntm.validateNotificationsAndReset();
661
662        // Test at 12:30am - next refresh should be 30 min later (1/4 into event 'id1').
663        currentTime = Utils.createTimeInMillis(0, 30, 0, day, month, year,
664                Time.getCurrentTimezone());
665        alarmMgr.expectAlarmTime(AlarmManager.RTC, currentTime + 30 * DateUtils.MINUTE_IN_MILLIS);
666        ntm.expectTestNotification(3, id1, PRIORITY_HIGH);
667        ntm.expectTestNotification(2, id3, PRIORITY_HIGH);
668        ntm.expectTestNotification(1, id4, PRIORITY_HIGH);
669        ntm.expectTestNotification(4, id2, PRIORITY_DEFAULT);
670        AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
671                currentTime, AlertService.MAX_NOTIFICATIONS);
672        ntm.validateNotificationsAndReset();
673
674        // Test at 5:55am - next refresh should be 20 min later (15 min after 'id3').
675        currentTime = Utils.createTimeInMillis(0, 55, 5, day, month, year,
676                Time.getCurrentTimezone());
677        alarmMgr.expectAlarmTime(AlarmManager.RTC, currentTime + 20 * DateUtils.MINUTE_IN_MILLIS);
678        ntm.expectTestNotification(2, id3, PRIORITY_HIGH);
679        ntm.expectTestNotification(1, id4, PRIORITY_HIGH);
680        ntm.expectTestNotification(3, id2, PRIORITY_DEFAULT);
681        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id1, PRIORITY_MIN);
682        AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
683                currentTime, AlertService.MAX_NOTIFICATIONS);
684        ntm.validateNotificationsAndReset();
685
686        // Test at 10:14am - next refresh should be 1 min later (15 min into event 'id4').
687        currentTime = Utils.createTimeInMillis(0, 14, 10, day, month, year,
688                Time.getCurrentTimezone());
689        alarmMgr.expectAlarmTime(AlarmManager.RTC, currentTime + 1 * DateUtils.MINUTE_IN_MILLIS);
690        ntm.expectTestNotification(1, id4, PRIORITY_HIGH);
691        ntm.expectTestNotification(2, id2, PRIORITY_DEFAULT);
692        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, new int[] {id3, id1},
693                PRIORITY_MIN);
694        AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
695                currentTime, AlertService.MAX_NOTIFICATIONS);
696        ntm.validateNotificationsAndReset();
697
698        // Test at 10:15am - next refresh should be tomorrow midnight (end of all day event 'id2').
699        currentTime = Utils.createTimeInMillis(0, 15, 10, day, month, year,
700                Time.getCurrentTimezone());
701        alarmMgr.expectAlarmTime(AlarmManager.RTC, Utils.createTimeInMillis(0, 0, 23,
702                tomorrow.monthDay, tomorrow.month, tomorrow.year, Time.getCurrentTimezone()));
703        ntm.expectTestNotification(1, id2, PRIORITY_DEFAULT);
704        ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID,
705                new int[] {id4, id3, id1}, PRIORITY_MIN);
706        AlertService.generateAlerts(mContext, ntm, alarmMgr, prefs, at.getAlertCursor(),
707                currentTime, AlertService.MAX_NOTIFICATIONS);
708        ntm.validateNotificationsAndReset();
709    }
710
711    private NotificationInfo createNotificationInfo(long eventId) {
712        return new NotificationInfo("eventName", "location", "description", 100L, 200L, eventId,
713                false, false);
714    }
715
716    private static long createTimeInMillis(int hour, int minute) {
717        return Utils.createTimeInMillis(0 /* second */, minute, hour, 1 /* day */, 1 /* month */,
718                2012 /* year */, Time.getCurrentTimezone());
719    }
720
721    @SmallTest
722    public void testProcessQuery_skipDeclinedDismissed() {
723        int declinedEventId = 1;
724        int dismissedEventId = 2;
725        int acceptedEventId = 3;
726        long acceptedStartTime = createTimeInMillis(10, 0);
727        long acceptedEndTime = createTimeInMillis(10, 30);
728
729        AlertsTable at = new AlertsTable();
730        at.addAlertRow(declinedEventId, SCHEDULED, DECLINED, 0, createTimeInMillis(9, 0),
731                createTimeInMillis(10, 0), 0);
732        at.addAlertRow(dismissedEventId, SCHEDULED, DISMISSED, 0, createTimeInMillis(9, 30),
733                createTimeInMillis(11, 0), 0);
734        at.addAlertRow(acceptedEventId, SCHEDULED, ACCEPTED, 1, acceptedStartTime, acceptedEndTime,
735                0);
736
737        ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
738        ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
739        ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
740        long currentTime = createTimeInMillis(5, 0);
741        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
742                mediumPriority, lowPriority);
743
744        assertEquals(0, lowPriority.size());
745        assertEquals(0, mediumPriority.size());
746        assertEquals(1, highPriority.size());
747        assertEquals(acceptedEventId, highPriority.get(0).eventId);
748        assertEquals(acceptedStartTime, highPriority.get(0).startMillis);
749        assertEquals(acceptedEndTime, highPriority.get(0).endMillis);
750        assertTrue(highPriority.get(0).allDay);
751    }
752
753    @SmallTest
754    public void testProcessQuery_newAlert() {
755        int scheduledAlertEventId = 1;
756        int firedAlertEventId = 2;
757
758        AlertsTable at = new AlertsTable();
759        at.addAlertRow(scheduledAlertEventId, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0),
760                createTimeInMillis(10, 0), 0);
761        at.addAlertRow(firedAlertEventId, FIRED, ACCEPTED, 0, createTimeInMillis(10, 0),
762                createTimeInMillis(10, 30), 0);
763
764        ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
765        ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
766        ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
767        long currentTime = createTimeInMillis(5, 0);
768        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
769                mediumPriority, lowPriority);
770
771        assertEquals(0, lowPriority.size());
772        assertEquals(0, mediumPriority.size());
773        assertEquals(2, highPriority.size());
774        assertEquals(scheduledAlertEventId, highPriority.get(0).eventId);
775        assertTrue("newAlert should be ON for scheduled alerts", highPriority.get(0).newAlert);
776        assertEquals(firedAlertEventId, highPriority.get(1).eventId);
777        assertFalse("newAlert should be OFF for fired alerts", highPriority.get(1).newAlert);
778    }
779
780    @SmallTest
781    public void testProcessQuery_recurringEvent() {
782        int eventId = 1;
783        long earlierStartTime = createTimeInMillis(10, 0);
784        long laterStartTime = createTimeInMillis(11, 0);
785
786        ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
787        ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
788        ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
789
790        AlertsTable at = new AlertsTable();
791        at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 0, laterStartTime,
792                laterStartTime + DateUtils.HOUR_IN_MILLIS, 0);
793        at.addAlertRow(eventId, FIRED, ACCEPTED, 0, earlierStartTime,
794                earlierStartTime + DateUtils.HOUR_IN_MILLIS, 0);
795
796        // Both events in the future: the earliest one should be chosen.
797        long currentTime = earlierStartTime - DateUtils.DAY_IN_MILLIS * 5;
798        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
799                mediumPriority, lowPriority);
800        assertEquals(0, lowPriority.size());
801        assertEquals(0, mediumPriority.size());
802        assertEquals(1, highPriority.size());
803        assertEquals("Recurring event with earlier start time expected", earlierStartTime,
804                highPriority.get(0).startMillis);
805
806        // Increment time just past the earlier event: the earlier one should be chosen.
807        highPriority.clear();
808        currentTime = earlierStartTime + DateUtils.MINUTE_IN_MILLIS * 10;
809        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
810                mediumPriority, lowPriority);
811        assertEquals(0, lowPriority.size());
812        assertEquals(0, mediumPriority.size());
813        assertEquals(1, highPriority.size());
814        assertEquals("Recurring event with earlier start time expected", earlierStartTime,
815                highPriority.get(0).startMillis);
816
817        // Increment time to 15 min past the earlier event: the later one should be chosen.
818        highPriority.clear();
819        currentTime = earlierStartTime + DateUtils.MINUTE_IN_MILLIS * 15;
820        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
821                mediumPriority, lowPriority);
822        assertEquals(0, lowPriority.size());
823        assertEquals(0, mediumPriority.size());
824        assertEquals(1, highPriority.size());
825        assertEquals("Recurring event with later start time expected", laterStartTime,
826                highPriority.get(0).startMillis);
827
828        // Both events in the past: the later one should be chosen (in the low priority bucket).
829        highPriority.clear();
830        currentTime = laterStartTime + DateUtils.DAY_IN_MILLIS * 5;
831        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
832                mediumPriority, lowPriority);
833        assertEquals(0, highPriority.size());
834        assertEquals(0, mediumPriority.size());
835        assertEquals(1, lowPriority.size());
836        assertEquals("Recurring event with later start time expected", laterStartTime,
837                lowPriority.get(0).startMillis);
838    }
839
840    @SmallTest
841    public void testProcessQuery_recurringAllDayEvent() {
842        int eventId = 1;
843        long day1 = Utils.createTimeInMillis(0, 0, 0, 1, 5, 2012, Time.TIMEZONE_UTC);
844        long day2 = Utils.createTimeInMillis(0, 0, 0, 2, 5, 2012, Time.TIMEZONE_UTC);
845
846        ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>();
847        ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>();
848        ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>();
849
850        AlertsTable at = new AlertsTable();
851        at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 1, day2, day2 + DateUtils.HOUR_IN_MILLIS * 24,
852                0);
853        at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 1, day1, day1 + DateUtils.HOUR_IN_MILLIS * 24,
854                0);
855
856        // Both events in the future: the earliest one should be chosen.
857        long currentTime = day1 - DateUtils.DAY_IN_MILLIS * 3;
858        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
859                mediumPriority, lowPriority);
860        assertEquals(0, lowPriority.size());
861        assertEquals(0, mediumPriority.size());
862        assertEquals(1, highPriority.size());
863        assertEquals("Recurring event with earlier start time expected", day1,
864                highPriority.get(0).startMillis);
865
866        // Increment time just past the earlier event (to 12:10am).  The earlier one should
867        // be chosen.
868        highPriority.clear();
869        currentTime = Utils.createTimeInMillis(0, 10, 0, 1, 5, 2012, Time.getCurrentTimezone());
870        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
871                mediumPriority, lowPriority);
872        assertEquals(0, lowPriority.size());
873        assertEquals(0, mediumPriority.size());
874        assertEquals(1, highPriority.size());
875        assertEquals("Recurring event with earlier start time expected", day1,
876                highPriority.get(0).startMillis);
877
878        // Increment time to 15 min past the earlier event: the later one should be chosen.
879        highPriority.clear();
880        currentTime = Utils.createTimeInMillis(0, 15, 0, 1, 5, 2012, Time.getCurrentTimezone());
881        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
882                mediumPriority, lowPriority);
883        assertEquals(0, lowPriority.size());
884        assertEquals(0, mediumPriority.size());
885        assertEquals(1, highPriority.size());
886        assertEquals("Recurring event with earlier start time expected", day2,
887                highPriority.get(0).startMillis);
888
889        // Both events in the past: the later one should be chosen (in the low priority bucket).
890        highPriority.clear();
891        currentTime = day2 + DateUtils.DAY_IN_MILLIS * 1;
892        AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority,
893                mediumPriority, lowPriority);
894        assertEquals(0, highPriority.size());
895        assertEquals(0, mediumPriority.size());
896        assertEquals(1, lowPriority.size());
897        assertEquals("Recurring event with later start time expected", day2,
898                lowPriority.get(0).startMillis);
899    }
900
901    @SmallTest
902    public void testRedistributeBuckets_withinLimits() throws Exception {
903        int maxNotifications = 3;
904        ArrayList<NotificationInfo> threeItemList = new ArrayList<NotificationInfo>();
905        threeItemList.add(createNotificationInfo(5));
906        threeItemList.add(createNotificationInfo(4));
907        threeItemList.add(createNotificationInfo(3));
908
909        // Test when max notifications at high priority.
910        ArrayList<NotificationInfo> high = threeItemList;
911        ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
912        ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
913        AlertService.redistributeBuckets(high, medium, low, maxNotifications);
914        assertEquals(3, high.size());
915        assertEquals(0, medium.size());
916        assertEquals(0, low.size());
917
918        // Test when max notifications at medium priority.
919        high = new ArrayList<NotificationInfo>();
920        medium = threeItemList;
921        low = new ArrayList<NotificationInfo>();
922        AlertService.redistributeBuckets(high, medium, low, maxNotifications);
923        assertEquals(0, high.size());
924        assertEquals(3, medium.size());
925        assertEquals(0, low.size());
926
927        // Test when max notifications at high and medium priority
928        high = new ArrayList<NotificationInfo>(threeItemList);
929        medium = new ArrayList<NotificationInfo>();
930        medium.add(high.remove(1));
931        low = new ArrayList<NotificationInfo>();
932        AlertService.redistributeBuckets(high, medium, low, maxNotifications);
933        assertEquals(2, high.size());
934        assertEquals(1, medium.size());
935        assertEquals(0, low.size());
936    }
937
938    @SmallTest
939    public void testRedistributeBuckets_tooManyHighPriority() throws Exception {
940        ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>();
941        ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
942        ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
943        high.add(createNotificationInfo(5));
944        high.add(createNotificationInfo(4));
945        high.add(createNotificationInfo(3));
946        high.add(createNotificationInfo(2));
947        high.add(createNotificationInfo(1));
948
949        // Invoke the method under test.
950        int maxNotifications = 3;
951        AlertService.redistributeBuckets(high, medium, low, maxNotifications);
952
953        // Verify some high priority were kicked out.
954        assertEquals(3, high.size());
955        assertEquals(3, high.get(0).eventId);
956        assertEquals(2, high.get(1).eventId);
957        assertEquals(1, high.get(2).eventId);
958
959        // Verify medium priority untouched.
960        assertEquals(0, medium.size());
961
962        // Verify the extras went to low priority.
963        assertEquals(2, low.size());
964        assertEquals(5, low.get(0).eventId);
965        assertEquals(4, low.get(1).eventId);
966    }
967
968    @SmallTest
969    public void testRedistributeBuckets_tooManyMediumPriority() throws Exception {
970        ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>();
971        ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
972        ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
973        high.add(createNotificationInfo(5));
974        high.add(createNotificationInfo(4));
975        medium.add(createNotificationInfo(3));
976        medium.add(createNotificationInfo(2));
977        medium.add(createNotificationInfo(1));
978
979        // Invoke the method under test.
980        int maxNotifications = 3;
981        AlertService.redistributeBuckets(high, medium, low, maxNotifications);
982
983        // Verify high priority untouched.
984        assertEquals(2, high.size());
985        assertEquals(5, high.get(0).eventId);
986        assertEquals(4, high.get(1).eventId);
987
988        // Verify some medium priority were kicked out (the ones near the end of the
989        // list).
990        assertEquals(1, medium.size());
991        assertEquals(3, medium.get(0).eventId);
992
993        // Verify the extras went to low priority.
994        assertEquals(2, low.size());
995        assertEquals(2, low.get(0).eventId);
996        assertEquals(1, low.get(1).eventId);
997    }
998
999    @SmallTest
1000    public void testRedistributeBuckets_tooManyHighMediumPriority() throws Exception {
1001        ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>();
1002        ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>();
1003        ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>();
1004        high.add(createNotificationInfo(8));
1005        high.add(createNotificationInfo(7));
1006        high.add(createNotificationInfo(6));
1007        high.add(createNotificationInfo(5));
1008        high.add(createNotificationInfo(4));
1009        medium.add(createNotificationInfo(3));
1010        medium.add(createNotificationInfo(2));
1011        medium.add(createNotificationInfo(1));
1012
1013        // Invoke the method under test.
1014        int maxNotifications = 3;
1015        AlertService.redistributeBuckets(high, medium, low, maxNotifications);
1016
1017        // Verify high priority.
1018        assertEquals(3, high.size());
1019        assertEquals(6, high.get(0).eventId);
1020        assertEquals(5, high.get(1).eventId);
1021        assertEquals(4, high.get(2).eventId);
1022
1023        // Verify some medium priority.
1024        assertEquals(0, medium.size());
1025
1026        // Verify low priority.
1027        assertEquals(5, low.size());
1028        assertEquals(8, low.get(0).eventId);
1029        assertEquals(7, low.get(1).eventId);
1030        assertEquals(3, low.get(2).eventId);
1031        assertEquals(2, low.get(3).eventId);
1032        assertEquals(1, low.get(4).eventId);
1033    }
1034}
1035