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