AlertServiceTest.java revision b5a2a61208857805cc053c6ac476d30100289279
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package com.android.calendar.alerts; 18 19import static android.app.Notification.PRIORITY_DEFAULT; 20import static android.app.Notification.PRIORITY_HIGH; 21import static android.app.Notification.PRIORITY_MIN; 22 23import android.content.SharedPreferences; 24import android.database.MatrixCursor; 25import android.provider.CalendarContract.Attendees; 26import android.provider.CalendarContract.CalendarAlerts; 27import android.test.AndroidTestCase; 28import android.test.suitebuilder.annotation.SmallTest; 29import android.test.suitebuilder.annotation.Smoke; 30import android.text.format.DateUtils; 31import android.text.format.Time; 32 33import com.android.calendar.GeneralPreferences; 34import com.android.calendar.alerts.AlertService.NotificationInfo; 35import com.android.calendar.alerts.AlertService.NotificationWrapper; 36 37import junit.framework.Assert; 38 39import java.util.ArrayList; 40import java.util.Arrays; 41import java.util.Map; 42import java.util.Set; 43 44public class AlertServiceTest extends AndroidTestCase { 45 46 class MockSharedPreferences implements SharedPreferences { 47 48 /* "always", "silent", depends on ringer mode */ 49 private String mVibrateWhen; 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 mVibrateWhen = "always"; 68 mRingtone = "/some/cool/ringtone"; 69 mPopup = true; 70 } 71 72 @Override 73 public boolean contains(String key) { 74 if (GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN.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_POPUP.equals(key)) { 83 if (mPopup == null) { 84 Assert.fail(GeneralPreferences.KEY_ALERTS_POPUP + " fetched more than once."); 85 } 86 boolean val = mPopup; 87 if (mStrict) { 88 mPopup = null; 89 } 90 return val; 91 } 92 throw new IllegalArgumentException(); 93 } 94 95 @Override 96 public String getString(String key, String defValue) { 97 if (GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN.equals(key)) { 98 if (mVibrateWhen == null) { 99 Assert.fail(GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN 100 + " fetched more than once."); 101 } 102 String val = mVibrateWhen; 103 if (mStrict) { 104 mVibrateWhen = null; 105 } 106 return val; 107 } 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 implements NotificationMgr { 256 // Expected notifications 257 NotificationInstance[] mNotifications; 258 259 // Flag to know which notification has been posted or canceled 260 boolean[] mDone; 261 262 // CalendarAlerts table 263 private ArrayList<Alert> mAlerts; 264 265 public NotificationTestManager(ArrayList<Alert> alerts, int maxNotifications) { 266 assertEquals(0, AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID); 267 mAlerts = alerts; 268 mNotifications = new NotificationInstance[maxNotifications + 1]; 269 } 270 271 public void expectTestNotification(int notificationId, int alertId, int highPriority) { 272 mNotifications[notificationId] = new NotificationInstance(alertId, highPriority); 273 } 274 275 public void expectTestNotification(int notificationId, int[] alertIds, int priority) { 276 mNotifications[notificationId] = new NotificationInstance(alertIds, priority); 277 } 278 279 private void verifyNotification(int id, NotificationWrapper nw) { 280 assertEquals(mNotifications[id].mPriority, nw.mNotification.priority); 281 282 NotificationInstance expected = mNotifications[id]; 283 if (expected.mAlertIdsInDigest == null) { 284 Alert a = mAlerts.get(expected.mAlertId); 285 assertEquals("Event ID", a.mEventId, nw.mEventId); 286 assertEquals("Begin time", a.mBegin, nw.mBegin); 287 assertEquals("End time", a.mEnd, nw.mEnd); 288 } else { 289 // Notification should be a digest. 290 assertNotNull("Posted notification not a digest as expected.", nw.mNw); 291 assertEquals("Number of notifications in digest not as expected.", 292 expected.mAlertIdsInDigest.length, nw.mNw.size()); 293 for (int i = 0; i < nw.mNw.size(); i++) { 294 Alert a = mAlerts.get(expected.mAlertIdsInDigest[i]); 295 assertEquals("Digest item " + i + ": Event ID not as expected", 296 a.mEventId, nw.mNw.get(i).mEventId); 297 assertEquals("Digest item " + i + ": Begin time in digest", 298 a.mBegin, nw.mNw.get(i).mBegin); 299 assertEquals("Digest item " + i + ": End time in digest", 300 a.mEnd, nw.mNw.get(i).mEnd); 301 } 302 } 303 } 304 305 public void validateNotificationsAndReset() { 306 for (int i = 0; i < mDone.length; i++) { 307 assertTrue("Notification id " + i + " has not been posted", mDone[i]); 308 } 309 Arrays.fill(mDone, false); 310 Arrays.fill(mNotifications, null); 311 } 312 313 /////////////////////////////// 314 // NotificationMgr methods 315 @Override 316 public void cancel(int id) { 317 if (mDone == null) { 318 mDone = new boolean[mNotifications.length]; 319 } 320 assertTrue("id out of bound: " + id, 0 <= id); 321 assertTrue("id out of bound: " + id, id < mDone.length); 322 assertFalse("id already used", mDone[id]); 323 mDone[id] = true; 324 assertNull("Unexpected cancel for id " + id, mNotifications[id]); 325 } 326 327 @Override 328 public void cancel(String tag, int id) { 329 throw new IllegalArgumentException(); 330 } 331 332 @Override 333 public void cancelAll() { 334 for (int i = 0; i < mNotifications.length; i++) { 335 assertNull("Expecting notification id " + i + ". Got cancelAll", mNotifications[i]); 336 337 if (mDone != null) { 338 assertFalse("Notification id " + i + " is done but got cancelAll", mDone[i]); 339 } 340 } 341 342 assertNull(mDone); // this should have been null since nothing 343 // should have been posted 344 mDone = new boolean[mNotifications.length]; 345 Arrays.fill(mDone, true); 346 } 347 348 @Override 349 public void notify(int id, NotificationWrapper nw) { 350 if (mDone == null) { 351 mDone = new boolean[mNotifications.length]; 352 } 353 assertTrue("id out of bound: " + id, 0 <= id); 354 assertTrue("id out of bound: " + id, id < mDone.length); 355 assertFalse("id already used", mDone[id]); 356 mDone[id] = true; 357 358 assertNotNull("Unexpected notify for id " + id, mNotifications[id]); 359 360 verifyNotification(id, nw); 361 } 362 363 @Override 364 public void notify(String tag, int id, NotificationWrapper nw) { 365 throw new IllegalArgumentException(); 366 } 367 } 368 369 // TODO 370 // Catch updates of new state, notify time, and received time 371 // Test ringer, vibrate, 372 // Test digest notifications 373 // Test intents, action email 374 // Catch alarmmgr calls 375 376 @Smoke 377 @SmallTest 378 public void testNoAlerts() { 379 MockSharedPreferences prefs = new MockSharedPreferences(); 380 AlertsTable at = new AlertsTable(); 381 NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, 382 AlertService.MAX_NOTIFICATIONS); 383 384 // Test no alert 385 long currentTime = 1000000; 386 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 387 AlertService.MAX_NOTIFICATIONS); 388 ntm.validateNotificationsAndReset(); 389 } 390 391 @Smoke 392 @SmallTest 393 public void testSingleAlert() { 394 MockSharedPreferences prefs = new MockSharedPreferences(); 395 AlertsTable at = new AlertsTable(); 396 NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, 397 AlertService.MAX_NOTIFICATIONS); 398 399 int id = at.addAlertRow(100, SCHEDULED, ACCEPTED, 0 /* all day */, 1300000, 2300000, 0); 400 401 // Test one up coming alert 402 long currentTime = 1000000; 403 ntm.expectTestNotification(1, id, PRIORITY_HIGH); 404 405 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 406 AlertService.MAX_NOTIFICATIONS); 407 ntm.validateNotificationsAndReset(); // This wipes out notification 408 // tests added so far 409 410 // Test half way into an event 411 currentTime = 2300000; 412 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id, PRIORITY_DEFAULT); 413 414 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 415 AlertService.MAX_NOTIFICATIONS); 416 ntm.validateNotificationsAndReset(); 417 418 // Test event ended 419 currentTime = 4300000; 420 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id, PRIORITY_MIN); 421 422 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 423 AlertService.MAX_NOTIFICATIONS); 424 ntm.validateNotificationsAndReset(); 425 } 426 427 @SmallTest 428 public void testMultipleAlerts() { 429 int maxNotifications = 10; 430 MockSharedPreferences prefs = new MockSharedPreferences(); 431 AlertsTable at = new AlertsTable(); 432 NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications); 433 434 // Current time - 5:00 435 long currentTime = createTimeInMillis(5, 0); 436 437 // Set up future alerts. The real query implementation sorts by descending start 438 // time so simulate that here with our order of adds to AlertsTable. 439 int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0), 440 createTimeInMillis(10, 0), 0); 441 int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0), 442 createTimeInMillis(9, 0), 0); 443 int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0), 444 createTimeInMillis(8, 0), 0); 445 446 // Set up concurrent alerts (that started recently). 447 int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0), 448 createTimeInMillis(5, 40), 0); 449 int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55), 450 createTimeInMillis(7, 30), 0); 451 int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50), 452 createTimeInMillis(4, 50), 0); 453 454 // Set up past alerts. 455 int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0), 456 createTimeInMillis(4, 0), 0); 457 int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0), 458 createTimeInMillis(3, 0), 0); 459 int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0), 460 createTimeInMillis(2, 0), 0); 461 462 // Check posted notifications. The order listed here is the order simulates the 463 // order in the real notification bar (last one posted appears on top), so these 464 // should be lowest start time on top. 465 ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent 466 ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent 467 ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent 468 ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future 469 ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future 470 ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future 471 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, 472 new int[] {id3, id2, id1}, PRIORITY_MIN); 473 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 474 maxNotifications); 475 ntm.validateNotificationsAndReset(); 476 477 // Increase time by 15 minutes to check that some concurrent events dropped 478 // to the low priority bucket. 479 currentTime = createTimeInMillis(5, 15); 480 ntm.expectTestNotification(4, id5, PRIORITY_HIGH); // concurrent 481 ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future 482 ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future 483 ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future 484 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, 485 new int[] {id6, id4, id3, id2, id1}, PRIORITY_MIN); 486 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 487 maxNotifications); 488 ntm.validateNotificationsAndReset(); 489 490 // Increase time so some of the previously future ones change state. 491 currentTime = createTimeInMillis(8, 15); 492 ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future 493 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, 494 new int[] {id8, id7, id6, id5, id4, id3, id2, id1}, PRIORITY_MIN); 495 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 496 maxNotifications); 497 ntm.validateNotificationsAndReset(); 498 } 499 500 @SmallTest 501 public void testMultipleAlerts_max() { 502 MockSharedPreferences prefs = new MockSharedPreferences(); 503 AlertsTable at = new AlertsTable(); 504 505 // Current time - 5:00 506 long currentTime = createTimeInMillis(5, 0); 507 508 // Set up future alerts. The real query implementation sorts by descending start 509 // time so simulate that here with our order of adds to AlertsTable. 510 int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0), 511 createTimeInMillis(10, 0), 0); 512 int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0), 513 createTimeInMillis(9, 0), 0); 514 int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0), 515 createTimeInMillis(8, 0), 0); 516 517 // Set up concurrent alerts (that started recently). 518 int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0), 519 createTimeInMillis(5, 40), 0); 520 int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55), 521 createTimeInMillis(7, 30), 0); 522 int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50), 523 createTimeInMillis(4, 50), 0); 524 525 // Set up past alerts. 526 int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0), 527 createTimeInMillis(4, 0), 0); 528 int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0), 529 createTimeInMillis(3, 0), 0); 530 int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0), 531 createTimeInMillis(2, 0), 0); 532 533 // Test when # alerts = max. 534 int maxNotifications = 6; 535 NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications); 536 ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent 537 ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent 538 ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent 539 ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future 540 ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future 541 ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future 542 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, 543 new int[] {id3, id2, id1}, PRIORITY_MIN); 544 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 545 maxNotifications); 546 ntm.validateNotificationsAndReset(); 547 548 // Test when # alerts > max. 549 maxNotifications = 4; 550 ntm = new NotificationTestManager(at.mAlerts, maxNotifications); 551 ntm.expectTestNotification(4, id4, PRIORITY_HIGH); // concurrent 552 ntm.expectTestNotification(3, id5, PRIORITY_HIGH); // concurrent 553 ntm.expectTestNotification(2, id6, PRIORITY_HIGH); // concurrent 554 ntm.expectTestNotification(1, id7, PRIORITY_HIGH); // future 555 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, 556 new int[] {id9, id8, id3, id2, id1}, PRIORITY_MIN); 557 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 558 maxNotifications); 559 ntm.validateNotificationsAndReset(); 560 } 561 562 /** 563 * Test that the SharedPreferences are only fetched once for each setting. 564 */ 565 @SmallTest 566 public void testMultipleAlerts_sharedPreferences() { 567 int maxNotifications = 10; 568 MockSharedPreferences prefs = new MockSharedPreferences(true /* strict mode */); 569 AlertsTable at = new AlertsTable(); 570 NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications); 571 572 // Current time - 5:00 573 long currentTime = createTimeInMillis(5, 0); 574 575 // Set up future alerts. The real query implementation sorts by descending start 576 // time so simulate that here with our order of adds to AlertsTable. 577 int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0), 578 createTimeInMillis(10, 0), 0); 579 int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0), 580 createTimeInMillis(9, 0), 0); 581 int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0), 582 createTimeInMillis(8, 0), 0); 583 584 // Set up concurrent alerts (that started recently). 585 int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0), 586 createTimeInMillis(5, 40), 0); 587 int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55), 588 createTimeInMillis(7, 30), 0); 589 int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50), 590 createTimeInMillis(4, 50), 0); 591 592 // Set up past alerts. 593 int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0), 594 createTimeInMillis(4, 0), 0); 595 int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0), 596 createTimeInMillis(3, 0), 0); 597 int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0), 598 createTimeInMillis(2, 0), 0); 599 600 // Expected notifications. 601 ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent 602 ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent 603 ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent 604 ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future 605 ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future 606 ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future 607 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, 608 new int[] {id3, id2, id1}, PRIORITY_MIN); 609 610 // If this does not result in a failure (MockSharedPreferences fails for duplicate 611 // queries), then test passes. 612 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 613 maxNotifications); 614 ntm.validateNotificationsAndReset(); 615 } 616 617 private NotificationInfo createNotificationInfo(long eventId) { 618 return new NotificationInfo("eventName", "location", "description", 100L, 200L, eventId, 619 false, false); 620 } 621 622 private static long createTimeInMillis(int hour, int minute) { 623 return createTimeInMillis(0 /* second */, minute, hour, 1 /* day */, 1 /* month */, 624 2012 /* year */, Time.getCurrentTimezone()); 625 } 626 627 private static long createTimeInMillis(int second, int minute, int hour, int monthDay, 628 int month, int year, String timezone) { 629 Time t = new Time(timezone); 630 t.set(second, minute, hour, monthDay, month, year); 631 t.normalize(false); 632 return t.toMillis(false); 633 } 634 635 @SmallTest 636 public void testProcessQuery_skipDeclinedDismissed() { 637 int declinedEventId = 1; 638 int dismissedEventId = 2; 639 int acceptedEventId = 3; 640 long acceptedStartTime = createTimeInMillis(10, 0); 641 long acceptedEndTime = createTimeInMillis(10, 30); 642 643 AlertsTable at = new AlertsTable(); 644 at.addAlertRow(declinedEventId, SCHEDULED, DECLINED, 0, createTimeInMillis(9, 0), 645 createTimeInMillis(10, 0), 0); 646 at.addAlertRow(dismissedEventId, SCHEDULED, DISMISSED, 0, createTimeInMillis(9, 30), 647 createTimeInMillis(11, 0), 0); 648 at.addAlertRow(acceptedEventId, SCHEDULED, ACCEPTED, 1, acceptedStartTime, acceptedEndTime, 649 0); 650 651 ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>(); 652 ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>(); 653 ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>(); 654 long currentTime = createTimeInMillis(5, 0); 655 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 656 mediumPriority, lowPriority); 657 658 assertEquals(0, lowPriority.size()); 659 assertEquals(0, mediumPriority.size()); 660 assertEquals(1, highPriority.size()); 661 assertEquals(acceptedEventId, highPriority.get(0).eventId); 662 assertEquals(acceptedStartTime, highPriority.get(0).startMillis); 663 assertEquals(acceptedEndTime, highPriority.get(0).endMillis); 664 assertTrue(highPriority.get(0).allDay); 665 } 666 667 @SmallTest 668 public void testProcessQuery_newAlert() { 669 int scheduledAlertEventId = 1; 670 int firedAlertEventId = 2; 671 672 AlertsTable at = new AlertsTable(); 673 at.addAlertRow(scheduledAlertEventId, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0), 674 createTimeInMillis(10, 0), 0); 675 at.addAlertRow(firedAlertEventId, FIRED, ACCEPTED, 0, createTimeInMillis(10, 0), 676 createTimeInMillis(10, 30), 0); 677 678 ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>(); 679 ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>(); 680 ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>(); 681 long currentTime = createTimeInMillis(5, 0); 682 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 683 mediumPriority, lowPriority); 684 685 assertEquals(0, lowPriority.size()); 686 assertEquals(0, mediumPriority.size()); 687 assertEquals(2, highPriority.size()); 688 assertEquals(scheduledAlertEventId, highPriority.get(0).eventId); 689 assertTrue("newAlert should be ON for scheduled alerts", highPriority.get(0).newAlert); 690 assertEquals(firedAlertEventId, highPriority.get(1).eventId); 691 assertFalse("newAlert should be OFF for fired alerts", highPriority.get(1).newAlert); 692 } 693 694 @SmallTest 695 public void testProcessQuery_recurringEvent() { 696 int eventId = 1; 697 long earlierStartTime = createTimeInMillis(10, 0); 698 long laterStartTime = createTimeInMillis(11, 0); 699 700 ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>(); 701 ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>(); 702 ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>(); 703 704 AlertsTable at = new AlertsTable(); 705 at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 0, laterStartTime, 706 laterStartTime + DateUtils.HOUR_IN_MILLIS, 0); 707 at.addAlertRow(eventId, FIRED, ACCEPTED, 0, earlierStartTime, 708 earlierStartTime + DateUtils.HOUR_IN_MILLIS, 0); 709 710 // Both events in the future: the earliest one should be chosen. 711 long currentTime = earlierStartTime - DateUtils.DAY_IN_MILLIS * 5; 712 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 713 mediumPriority, lowPriority); 714 assertEquals(0, lowPriority.size()); 715 assertEquals(0, mediumPriority.size()); 716 assertEquals(1, highPriority.size()); 717 assertEquals("Recurring event with earlier start time expected", earlierStartTime, 718 highPriority.get(0).startMillis); 719 720 // Increment time just past the earlier event: the earlier one should be chosen. 721 highPriority.clear(); 722 currentTime = earlierStartTime + DateUtils.MINUTE_IN_MILLIS * 10; 723 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 724 mediumPriority, lowPriority); 725 assertEquals(0, lowPriority.size()); 726 assertEquals(0, mediumPriority.size()); 727 assertEquals(1, highPriority.size()); 728 assertEquals("Recurring event with earlier start time expected", earlierStartTime, 729 highPriority.get(0).startMillis); 730 731 // Increment time to 15 min past the earlier event: the later one should be chosen. 732 highPriority.clear(); 733 currentTime = earlierStartTime + DateUtils.MINUTE_IN_MILLIS * 15; 734 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 735 mediumPriority, lowPriority); 736 assertEquals(0, lowPriority.size()); 737 assertEquals(0, mediumPriority.size()); 738 assertEquals(1, highPriority.size()); 739 assertEquals("Recurring event with later start time expected", laterStartTime, 740 highPriority.get(0).startMillis); 741 742 // Both events in the past: the later one should be chosen (in the low priority bucket). 743 highPriority.clear(); 744 currentTime = laterStartTime + DateUtils.DAY_IN_MILLIS * 5; 745 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 746 mediumPriority, lowPriority); 747 assertEquals(0, highPriority.size()); 748 assertEquals(0, mediumPriority.size()); 749 assertEquals(1, lowPriority.size()); 750 assertEquals("Recurring event with later start time expected", laterStartTime, 751 lowPriority.get(0).startMillis); 752 } 753 754 @SmallTest 755 public void testProcessQuery_recurringAllDayEvent() { 756 int eventId = 1; 757 long day1 = createTimeInMillis(0, 0, 0, 1, 5, 2012, Time.TIMEZONE_UTC); 758 long day2 = createTimeInMillis(0, 0, 0, 2, 5, 2012, Time.TIMEZONE_UTC); 759 760 ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>(); 761 ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>(); 762 ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>(); 763 764 AlertsTable at = new AlertsTable(); 765 at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 1, day2, day2 + DateUtils.HOUR_IN_MILLIS * 24, 766 0); 767 at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 1, day1, day1 + DateUtils.HOUR_IN_MILLIS * 24, 768 0); 769 770 // Both events in the future: the earliest one should be chosen. 771 long currentTime = day1 - DateUtils.DAY_IN_MILLIS * 3; 772 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 773 mediumPriority, lowPriority); 774 assertEquals(0, lowPriority.size()); 775 assertEquals(0, mediumPriority.size()); 776 assertEquals(1, highPriority.size()); 777 assertEquals("Recurring event with earlier start time expected", day1, 778 highPriority.get(0).startMillis); 779 780 // Increment time just past the earlier event (to 12:10am). The earlier one should 781 // be chosen. 782 highPriority.clear(); 783 currentTime = createTimeInMillis(0, 10, 0, 1, 5, 2012, Time.getCurrentTimezone()); 784 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 785 mediumPriority, lowPriority); 786 assertEquals(0, lowPriority.size()); 787 assertEquals(0, mediumPriority.size()); 788 assertEquals(1, highPriority.size()); 789 assertEquals("Recurring event with earlier start time expected", day1, 790 highPriority.get(0).startMillis); 791 792 // Increment time to 15 min past the earlier event: the later one should be chosen. 793 highPriority.clear(); 794 currentTime = createTimeInMillis(0, 15, 0, 1, 5, 2012, Time.getCurrentTimezone()); 795 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 796 mediumPriority, lowPriority); 797 assertEquals(0, lowPriority.size()); 798 assertEquals(0, mediumPriority.size()); 799 assertEquals(1, highPriority.size()); 800 assertEquals("Recurring event with earlier start time expected", day2, 801 highPriority.get(0).startMillis); 802 803 // Both events in the past: the later one should be chosen (in the low priority bucket). 804 highPriority.clear(); 805 currentTime = day2 + DateUtils.DAY_IN_MILLIS * 1; 806 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 807 mediumPriority, lowPriority); 808 assertEquals(0, highPriority.size()); 809 assertEquals(0, mediumPriority.size()); 810 assertEquals(1, lowPriority.size()); 811 assertEquals("Recurring event with later start time expected", day2, 812 lowPriority.get(0).startMillis); 813 } 814 815 @SmallTest 816 public void testRedistributeBuckets_withinLimits() throws Exception { 817 int maxNotifications = 3; 818 ArrayList<NotificationInfo> threeItemList = new ArrayList<NotificationInfo>(); 819 threeItemList.add(createNotificationInfo(5)); 820 threeItemList.add(createNotificationInfo(4)); 821 threeItemList.add(createNotificationInfo(3)); 822 823 // Test when max notifications at high priority. 824 ArrayList<NotificationInfo> high = threeItemList; 825 ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>(); 826 ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>(); 827 AlertService.redistributeBuckets(high, medium, low, maxNotifications); 828 assertEquals(3, high.size()); 829 assertEquals(0, medium.size()); 830 assertEquals(0, low.size()); 831 832 // Test when max notifications at medium priority. 833 high = new ArrayList<NotificationInfo>(); 834 medium = threeItemList; 835 low = new ArrayList<NotificationInfo>(); 836 AlertService.redistributeBuckets(high, medium, low, maxNotifications); 837 assertEquals(0, high.size()); 838 assertEquals(3, medium.size()); 839 assertEquals(0, low.size()); 840 841 // Test when max notifications at high and medium priority 842 high = new ArrayList<NotificationInfo>(threeItemList); 843 medium = new ArrayList<NotificationInfo>(); 844 medium.add(high.remove(1)); 845 low = new ArrayList<NotificationInfo>(); 846 AlertService.redistributeBuckets(high, medium, low, maxNotifications); 847 assertEquals(2, high.size()); 848 assertEquals(1, medium.size()); 849 assertEquals(0, low.size()); 850 } 851 852 @SmallTest 853 public void testRedistributeBuckets_tooManyHighPriority() throws Exception { 854 ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>(); 855 ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>(); 856 ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>(); 857 high.add(createNotificationInfo(5)); 858 high.add(createNotificationInfo(4)); 859 high.add(createNotificationInfo(3)); 860 high.add(createNotificationInfo(2)); 861 high.add(createNotificationInfo(1)); 862 863 // Invoke the method under test. 864 int maxNotifications = 3; 865 AlertService.redistributeBuckets(high, medium, low, maxNotifications); 866 867 // Verify some high priority were kicked out. 868 assertEquals(3, high.size()); 869 assertEquals(3, high.get(0).eventId); 870 assertEquals(2, high.get(1).eventId); 871 assertEquals(1, high.get(2).eventId); 872 873 // Verify medium priority untouched. 874 assertEquals(0, medium.size()); 875 876 // Verify the extras went to low priority. 877 assertEquals(2, low.size()); 878 assertEquals(5, low.get(0).eventId); 879 assertEquals(4, low.get(1).eventId); 880 } 881 882 @SmallTest 883 public void testRedistributeBuckets_tooManyMediumPriority() throws Exception { 884 ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>(); 885 ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>(); 886 ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>(); 887 high.add(createNotificationInfo(5)); 888 high.add(createNotificationInfo(4)); 889 medium.add(createNotificationInfo(3)); 890 medium.add(createNotificationInfo(2)); 891 medium.add(createNotificationInfo(1)); 892 893 // Invoke the method under test. 894 int maxNotifications = 3; 895 AlertService.redistributeBuckets(high, medium, low, maxNotifications); 896 897 // Verify high priority untouched. 898 assertEquals(2, high.size()); 899 assertEquals(5, high.get(0).eventId); 900 assertEquals(4, high.get(1).eventId); 901 902 // Verify some medium priority were kicked out (the ones near the end of the 903 // list). 904 assertEquals(1, medium.size()); 905 assertEquals(3, medium.get(0).eventId); 906 907 // Verify the extras went to low priority. 908 assertEquals(2, low.size()); 909 assertEquals(2, low.get(0).eventId); 910 assertEquals(1, low.get(1).eventId); 911 } 912 913 @SmallTest 914 public void testRedistributeBuckets_tooManyHighMediumPriority() throws Exception { 915 ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>(); 916 ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>(); 917 ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>(); 918 high.add(createNotificationInfo(8)); 919 high.add(createNotificationInfo(7)); 920 high.add(createNotificationInfo(6)); 921 high.add(createNotificationInfo(5)); 922 high.add(createNotificationInfo(4)); 923 medium.add(createNotificationInfo(3)); 924 medium.add(createNotificationInfo(2)); 925 medium.add(createNotificationInfo(1)); 926 927 // Invoke the method under test. 928 int maxNotifications = 3; 929 AlertService.redistributeBuckets(high, medium, low, maxNotifications); 930 931 // Verify high priority. 932 assertEquals(3, high.size()); 933 assertEquals(6, high.get(0).eventId); 934 assertEquals(5, high.get(1).eventId); 935 assertEquals(4, high.get(2).eventId); 936 937 // Verify some medium priority. 938 assertEquals(0, medium.size()); 939 940 // Verify low priority. 941 assertEquals(5, low.size()); 942 assertEquals(8, low.get(0).eventId); 943 assertEquals(7, low.get(1).eventId); 944 assertEquals(3, low.get(2).eventId); 945 assertEquals(2, low.get(3).eventId); 946 assertEquals(1, low.get(4).eventId); 947 } 948} 949