AlertServiceTest.java revision 948c590ced6854d2fbe9dc765db4ae8d63646664
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 extends 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 notify(int id, NotificationWrapper nw) { 329 if (mDone == null) { 330 mDone = new boolean[mNotifications.length]; 331 } 332 assertTrue("id out of bound: " + id, 0 <= id); 333 assertTrue("id out of bound: " + id, id < mDone.length); 334 assertFalse("id already used", mDone[id]); 335 mDone[id] = true; 336 337 assertNotNull("Unexpected notify for id " + id, mNotifications[id]); 338 339 verifyNotification(id, nw); 340 } 341 } 342 343 // TODO 344 // Catch updates of new state, notify time, and received time 345 // Test ringer, vibrate, 346 // Test digest notifications 347 // Test intents, action email 348 // Catch alarmmgr calls 349 350 @Smoke 351 @SmallTest 352 public void testNoAlerts() { 353 MockSharedPreferences prefs = new MockSharedPreferences(); 354 AlertsTable at = new AlertsTable(); 355 NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, 356 AlertService.MAX_NOTIFICATIONS); 357 358 // Test no alert 359 long currentTime = 1000000; 360 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 361 AlertService.MAX_NOTIFICATIONS); 362 ntm.validateNotificationsAndReset(); 363 } 364 365 @Smoke 366 @SmallTest 367 public void testSingleAlert() { 368 MockSharedPreferences prefs = new MockSharedPreferences(); 369 AlertsTable at = new AlertsTable(); 370 NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, 371 AlertService.MAX_NOTIFICATIONS); 372 373 int id = at.addAlertRow(100, SCHEDULED, ACCEPTED, 0 /* all day */, 1300000, 2300000, 0); 374 375 // Test one up coming alert 376 long currentTime = 1000000; 377 ntm.expectTestNotification(1, id, PRIORITY_HIGH); 378 379 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 380 AlertService.MAX_NOTIFICATIONS); 381 ntm.validateNotificationsAndReset(); // This wipes out notification 382 // tests added so far 383 384 // Test half way into an event 385 currentTime = 2300000; 386 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id, PRIORITY_DEFAULT); 387 388 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 389 AlertService.MAX_NOTIFICATIONS); 390 ntm.validateNotificationsAndReset(); 391 392 // Test event ended 393 currentTime = 4300000; 394 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, id, PRIORITY_MIN); 395 396 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 397 AlertService.MAX_NOTIFICATIONS); 398 ntm.validateNotificationsAndReset(); 399 } 400 401 @SmallTest 402 public void testMultipleAlerts() { 403 int maxNotifications = 10; 404 MockSharedPreferences prefs = new MockSharedPreferences(); 405 AlertsTable at = new AlertsTable(); 406 NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications); 407 408 // Current time - 5:00 409 long currentTime = createTimeInMillis(5, 0); 410 411 // Set up future alerts. The real query implementation sorts by descending start 412 // time so simulate that here with our order of adds to AlertsTable. 413 int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0), 414 createTimeInMillis(10, 0), 0); 415 int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0), 416 createTimeInMillis(9, 0), 0); 417 int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0), 418 createTimeInMillis(8, 0), 0); 419 420 // Set up concurrent alerts (that started recently). 421 int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0), 422 createTimeInMillis(5, 40), 0); 423 int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55), 424 createTimeInMillis(7, 30), 0); 425 int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50), 426 createTimeInMillis(4, 50), 0); 427 428 // Set up past alerts. 429 int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0), 430 createTimeInMillis(4, 0), 0); 431 int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0), 432 createTimeInMillis(3, 0), 0); 433 int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0), 434 createTimeInMillis(2, 0), 0); 435 436 // Check posted notifications. The order listed here is the order simulates the 437 // order in the real notification bar (last one posted appears on top), so these 438 // should be lowest start time on top. 439 ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent 440 ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent 441 ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent 442 ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future 443 ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future 444 ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future 445 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, 446 new int[] {id3, id2, id1}, PRIORITY_MIN); 447 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 448 maxNotifications); 449 ntm.validateNotificationsAndReset(); 450 451 // Increase time by 15 minutes to check that some concurrent events dropped 452 // to the low priority bucket. 453 currentTime = createTimeInMillis(5, 15); 454 ntm.expectTestNotification(4, id5, PRIORITY_HIGH); // concurrent 455 ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future 456 ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future 457 ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future 458 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, 459 new int[] {id6, id4, id3, id2, id1}, PRIORITY_MIN); 460 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 461 maxNotifications); 462 ntm.validateNotificationsAndReset(); 463 464 // Increase time so some of the previously future ones change state. 465 currentTime = createTimeInMillis(8, 15); 466 ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future 467 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, 468 new int[] {id8, id7, id6, id5, id4, id3, id2, id1}, PRIORITY_MIN); 469 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 470 maxNotifications); 471 ntm.validateNotificationsAndReset(); 472 } 473 474 @SmallTest 475 public void testMultipleAlerts_max() { 476 MockSharedPreferences prefs = new MockSharedPreferences(); 477 AlertsTable at = new AlertsTable(); 478 479 // Current time - 5:00 480 long currentTime = createTimeInMillis(5, 0); 481 482 // Set up future alerts. The real query implementation sorts by descending start 483 // time so simulate that here with our order of adds to AlertsTable. 484 int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0), 485 createTimeInMillis(10, 0), 0); 486 int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0), 487 createTimeInMillis(9, 0), 0); 488 int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0), 489 createTimeInMillis(8, 0), 0); 490 491 // Set up concurrent alerts (that started recently). 492 int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0), 493 createTimeInMillis(5, 40), 0); 494 int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55), 495 createTimeInMillis(7, 30), 0); 496 int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50), 497 createTimeInMillis(4, 50), 0); 498 499 // Set up past alerts. 500 int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0), 501 createTimeInMillis(4, 0), 0); 502 int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0), 503 createTimeInMillis(3, 0), 0); 504 int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0), 505 createTimeInMillis(2, 0), 0); 506 507 // Test when # alerts = max. 508 int maxNotifications = 6; 509 NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications); 510 ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent 511 ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent 512 ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent 513 ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future 514 ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future 515 ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future 516 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, 517 new int[] {id3, id2, id1}, PRIORITY_MIN); 518 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 519 maxNotifications); 520 ntm.validateNotificationsAndReset(); 521 522 // Test when # alerts > max. 523 maxNotifications = 4; 524 ntm = new NotificationTestManager(at.mAlerts, maxNotifications); 525 ntm.expectTestNotification(4, id4, PRIORITY_HIGH); // concurrent 526 ntm.expectTestNotification(3, id5, PRIORITY_HIGH); // concurrent 527 ntm.expectTestNotification(2, id6, PRIORITY_HIGH); // concurrent 528 ntm.expectTestNotification(1, id7, PRIORITY_HIGH); // future 529 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, 530 new int[] {id9, id8, id3, id2, id1}, PRIORITY_MIN); 531 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 532 maxNotifications); 533 ntm.validateNotificationsAndReset(); 534 } 535 536 /** 537 * Test that the SharedPreferences are only fetched once for each setting. 538 */ 539 @SmallTest 540 public void testMultipleAlerts_sharedPreferences() { 541 int maxNotifications = 10; 542 MockSharedPreferences prefs = new MockSharedPreferences(true /* strict mode */); 543 AlertsTable at = new AlertsTable(); 544 NotificationTestManager ntm = new NotificationTestManager(at.mAlerts, maxNotifications); 545 546 // Current time - 5:00 547 long currentTime = createTimeInMillis(5, 0); 548 549 // Set up future alerts. The real query implementation sorts by descending start 550 // time so simulate that here with our order of adds to AlertsTable. 551 int id9 = at.addAlertRow(9, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0), 552 createTimeInMillis(10, 0), 0); 553 int id8 = at.addAlertRow(8, SCHEDULED, ACCEPTED, 0, createTimeInMillis(8, 0), 554 createTimeInMillis(9, 0), 0); 555 int id7 = at.addAlertRow(7, SCHEDULED, ACCEPTED, 0, createTimeInMillis(7, 0), 556 createTimeInMillis(8, 0), 0); 557 558 // Set up concurrent alerts (that started recently). 559 int id6 = at.addAlertRow(6, SCHEDULED, ACCEPTED, 0, createTimeInMillis(5, 0), 560 createTimeInMillis(5, 40), 0); 561 int id5 = at.addAlertRow(5, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 55), 562 createTimeInMillis(7, 30), 0); 563 int id4 = at.addAlertRow(4, SCHEDULED, ACCEPTED, 0, createTimeInMillis(4, 50), 564 createTimeInMillis(4, 50), 0); 565 566 // Set up past alerts. 567 int id3 = at.addAlertRow(3, SCHEDULED, ACCEPTED, 0, createTimeInMillis(3, 0), 568 createTimeInMillis(4, 0), 0); 569 int id2 = at.addAlertRow(2, SCHEDULED, ACCEPTED, 0, createTimeInMillis(2, 0), 570 createTimeInMillis(3, 0), 0); 571 int id1 = at.addAlertRow(1, SCHEDULED, ACCEPTED, 0, createTimeInMillis(1, 0), 572 createTimeInMillis(2, 0), 0); 573 574 // Expected notifications. 575 ntm.expectTestNotification(6, id4, PRIORITY_HIGH); // concurrent 576 ntm.expectTestNotification(5, id5, PRIORITY_HIGH); // concurrent 577 ntm.expectTestNotification(4, id6, PRIORITY_HIGH); // concurrent 578 ntm.expectTestNotification(3, id7, PRIORITY_HIGH); // future 579 ntm.expectTestNotification(2, id8, PRIORITY_HIGH); // future 580 ntm.expectTestNotification(1, id9, PRIORITY_HIGH); // future 581 ntm.expectTestNotification(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, 582 new int[] {id3, id2, id1}, PRIORITY_MIN); 583 584 // If this does not result in a failure (MockSharedPreferences fails for duplicate 585 // queries), then test passes. 586 AlertService.generateAlerts(mContext, ntm, prefs, at.getAlertCursor(), currentTime, 587 maxNotifications); 588 ntm.validateNotificationsAndReset(); 589 } 590 591 private NotificationInfo createNotificationInfo(long eventId) { 592 return new NotificationInfo("eventName", "location", "description", 100L, 200L, eventId, 593 false, false); 594 } 595 596 private static long createTimeInMillis(int hour, int minute) { 597 return createTimeInMillis(0 /* second */, minute, hour, 1 /* day */, 1 /* month */, 598 2012 /* year */, Time.getCurrentTimezone()); 599 } 600 601 private static long createTimeInMillis(int second, int minute, int hour, int monthDay, 602 int month, int year, String timezone) { 603 Time t = new Time(timezone); 604 t.set(second, minute, hour, monthDay, month, year); 605 t.normalize(false); 606 return t.toMillis(false); 607 } 608 609 @SmallTest 610 public void testProcessQuery_skipDeclinedDismissed() { 611 int declinedEventId = 1; 612 int dismissedEventId = 2; 613 int acceptedEventId = 3; 614 long acceptedStartTime = createTimeInMillis(10, 0); 615 long acceptedEndTime = createTimeInMillis(10, 30); 616 617 AlertsTable at = new AlertsTable(); 618 at.addAlertRow(declinedEventId, SCHEDULED, DECLINED, 0, createTimeInMillis(9, 0), 619 createTimeInMillis(10, 0), 0); 620 at.addAlertRow(dismissedEventId, SCHEDULED, DISMISSED, 0, createTimeInMillis(9, 30), 621 createTimeInMillis(11, 0), 0); 622 at.addAlertRow(acceptedEventId, SCHEDULED, ACCEPTED, 1, acceptedStartTime, acceptedEndTime, 623 0); 624 625 ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>(); 626 ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>(); 627 ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>(); 628 long currentTime = createTimeInMillis(5, 0); 629 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 630 mediumPriority, lowPriority); 631 632 assertEquals(0, lowPriority.size()); 633 assertEquals(0, mediumPriority.size()); 634 assertEquals(1, highPriority.size()); 635 assertEquals(acceptedEventId, highPriority.get(0).eventId); 636 assertEquals(acceptedStartTime, highPriority.get(0).startMillis); 637 assertEquals(acceptedEndTime, highPriority.get(0).endMillis); 638 assertTrue(highPriority.get(0).allDay); 639 } 640 641 @SmallTest 642 public void testProcessQuery_newAlert() { 643 int scheduledAlertEventId = 1; 644 int firedAlertEventId = 2; 645 646 AlertsTable at = new AlertsTable(); 647 at.addAlertRow(scheduledAlertEventId, SCHEDULED, ACCEPTED, 0, createTimeInMillis(9, 0), 648 createTimeInMillis(10, 0), 0); 649 at.addAlertRow(firedAlertEventId, FIRED, ACCEPTED, 0, createTimeInMillis(10, 0), 650 createTimeInMillis(10, 30), 0); 651 652 ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>(); 653 ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>(); 654 ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>(); 655 long currentTime = createTimeInMillis(5, 0); 656 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 657 mediumPriority, lowPriority); 658 659 assertEquals(0, lowPriority.size()); 660 assertEquals(0, mediumPriority.size()); 661 assertEquals(2, highPriority.size()); 662 assertEquals(scheduledAlertEventId, highPriority.get(0).eventId); 663 assertTrue("newAlert should be ON for scheduled alerts", highPriority.get(0).newAlert); 664 assertEquals(firedAlertEventId, highPriority.get(1).eventId); 665 assertFalse("newAlert should be OFF for fired alerts", highPriority.get(1).newAlert); 666 } 667 668 @SmallTest 669 public void testProcessQuery_recurringEvent() { 670 int eventId = 1; 671 long earlierStartTime = createTimeInMillis(10, 0); 672 long laterStartTime = createTimeInMillis(11, 0); 673 674 ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>(); 675 ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>(); 676 ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>(); 677 678 AlertsTable at = new AlertsTable(); 679 at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 0, laterStartTime, 680 laterStartTime + DateUtils.HOUR_IN_MILLIS, 0); 681 at.addAlertRow(eventId, FIRED, ACCEPTED, 0, earlierStartTime, 682 earlierStartTime + DateUtils.HOUR_IN_MILLIS, 0); 683 684 // Both events in the future: the earliest one should be chosen. 685 long currentTime = earlierStartTime - DateUtils.DAY_IN_MILLIS * 5; 686 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 687 mediumPriority, lowPriority); 688 assertEquals(0, lowPriority.size()); 689 assertEquals(0, mediumPriority.size()); 690 assertEquals(1, highPriority.size()); 691 assertEquals("Recurring event with earlier start time expected", earlierStartTime, 692 highPriority.get(0).startMillis); 693 694 // Increment time just past the earlier event: the earlier one should be chosen. 695 highPriority.clear(); 696 currentTime = earlierStartTime + DateUtils.MINUTE_IN_MILLIS * 10; 697 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 698 mediumPriority, lowPriority); 699 assertEquals(0, lowPriority.size()); 700 assertEquals(0, mediumPriority.size()); 701 assertEquals(1, highPriority.size()); 702 assertEquals("Recurring event with earlier start time expected", earlierStartTime, 703 highPriority.get(0).startMillis); 704 705 // Increment time to 15 min past the earlier event: the later one should be chosen. 706 highPriority.clear(); 707 currentTime = earlierStartTime + DateUtils.MINUTE_IN_MILLIS * 15; 708 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 709 mediumPriority, lowPriority); 710 assertEquals(0, lowPriority.size()); 711 assertEquals(0, mediumPriority.size()); 712 assertEquals(1, highPriority.size()); 713 assertEquals("Recurring event with later start time expected", laterStartTime, 714 highPriority.get(0).startMillis); 715 716 // Both events in the past: the later one should be chosen (in the low priority bucket). 717 highPriority.clear(); 718 currentTime = laterStartTime + DateUtils.DAY_IN_MILLIS * 5; 719 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 720 mediumPriority, lowPriority); 721 assertEquals(0, highPriority.size()); 722 assertEquals(0, mediumPriority.size()); 723 assertEquals(1, lowPriority.size()); 724 assertEquals("Recurring event with later start time expected", laterStartTime, 725 lowPriority.get(0).startMillis); 726 } 727 728 @SmallTest 729 public void testProcessQuery_recurringAllDayEvent() { 730 int eventId = 1; 731 long day1 = createTimeInMillis(0, 0, 0, 1, 5, 2012, Time.TIMEZONE_UTC); 732 long day2 = createTimeInMillis(0, 0, 0, 2, 5, 2012, Time.TIMEZONE_UTC); 733 734 ArrayList<NotificationInfo> highPriority = new ArrayList<NotificationInfo>(); 735 ArrayList<NotificationInfo> mediumPriority = new ArrayList<NotificationInfo>(); 736 ArrayList<NotificationInfo> lowPriority = new ArrayList<NotificationInfo>(); 737 738 AlertsTable at = new AlertsTable(); 739 at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 1, day2, day2 + DateUtils.HOUR_IN_MILLIS * 24, 740 0); 741 at.addAlertRow(eventId, SCHEDULED, ACCEPTED, 1, day1, day1 + DateUtils.HOUR_IN_MILLIS * 24, 742 0); 743 744 // Both events in the future: the earliest one should be chosen. 745 long currentTime = day1 - DateUtils.DAY_IN_MILLIS * 3; 746 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 747 mediumPriority, lowPriority); 748 assertEquals(0, lowPriority.size()); 749 assertEquals(0, mediumPriority.size()); 750 assertEquals(1, highPriority.size()); 751 assertEquals("Recurring event with earlier start time expected", day1, 752 highPriority.get(0).startMillis); 753 754 // Increment time just past the earlier event (to 12:10am). The earlier one should 755 // be chosen. 756 highPriority.clear(); 757 currentTime = createTimeInMillis(0, 10, 0, 1, 5, 2012, Time.getCurrentTimezone()); 758 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 759 mediumPriority, lowPriority); 760 assertEquals(0, lowPriority.size()); 761 assertEquals(0, mediumPriority.size()); 762 assertEquals(1, highPriority.size()); 763 assertEquals("Recurring event with earlier start time expected", day1, 764 highPriority.get(0).startMillis); 765 766 // Increment time to 15 min past the earlier event: the later one should be chosen. 767 highPriority.clear(); 768 currentTime = createTimeInMillis(0, 15, 0, 1, 5, 2012, Time.getCurrentTimezone()); 769 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 770 mediumPriority, lowPriority); 771 assertEquals(0, lowPriority.size()); 772 assertEquals(0, mediumPriority.size()); 773 assertEquals(1, highPriority.size()); 774 assertEquals("Recurring event with earlier start time expected", day2, 775 highPriority.get(0).startMillis); 776 777 // Both events in the past: the later one should be chosen (in the low priority bucket). 778 highPriority.clear(); 779 currentTime = day2 + DateUtils.DAY_IN_MILLIS * 1; 780 AlertService.processQuery(at.getAlertCursor(), mContext, currentTime, highPriority, 781 mediumPriority, lowPriority); 782 assertEquals(0, highPriority.size()); 783 assertEquals(0, mediumPriority.size()); 784 assertEquals(1, lowPriority.size()); 785 assertEquals("Recurring event with later start time expected", day2, 786 lowPriority.get(0).startMillis); 787 } 788 789 @SmallTest 790 public void testRedistributeBuckets_withinLimits() throws Exception { 791 int maxNotifications = 3; 792 ArrayList<NotificationInfo> threeItemList = new ArrayList<NotificationInfo>(); 793 threeItemList.add(createNotificationInfo(5)); 794 threeItemList.add(createNotificationInfo(4)); 795 threeItemList.add(createNotificationInfo(3)); 796 797 // Test when max notifications at high priority. 798 ArrayList<NotificationInfo> high = threeItemList; 799 ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>(); 800 ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>(); 801 AlertService.redistributeBuckets(high, medium, low, maxNotifications); 802 assertEquals(3, high.size()); 803 assertEquals(0, medium.size()); 804 assertEquals(0, low.size()); 805 806 // Test when max notifications at medium priority. 807 high = new ArrayList<NotificationInfo>(); 808 medium = threeItemList; 809 low = new ArrayList<NotificationInfo>(); 810 AlertService.redistributeBuckets(high, medium, low, maxNotifications); 811 assertEquals(0, high.size()); 812 assertEquals(3, medium.size()); 813 assertEquals(0, low.size()); 814 815 // Test when max notifications at high and medium priority 816 high = new ArrayList<NotificationInfo>(threeItemList); 817 medium = new ArrayList<NotificationInfo>(); 818 medium.add(high.remove(1)); 819 low = new ArrayList<NotificationInfo>(); 820 AlertService.redistributeBuckets(high, medium, low, maxNotifications); 821 assertEquals(2, high.size()); 822 assertEquals(1, medium.size()); 823 assertEquals(0, low.size()); 824 } 825 826 @SmallTest 827 public void testRedistributeBuckets_tooManyHighPriority() throws Exception { 828 ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>(); 829 ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>(); 830 ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>(); 831 high.add(createNotificationInfo(5)); 832 high.add(createNotificationInfo(4)); 833 high.add(createNotificationInfo(3)); 834 high.add(createNotificationInfo(2)); 835 high.add(createNotificationInfo(1)); 836 837 // Invoke the method under test. 838 int maxNotifications = 3; 839 AlertService.redistributeBuckets(high, medium, low, maxNotifications); 840 841 // Verify some high priority were kicked out. 842 assertEquals(3, high.size()); 843 assertEquals(3, high.get(0).eventId); 844 assertEquals(2, high.get(1).eventId); 845 assertEquals(1, high.get(2).eventId); 846 847 // Verify medium priority untouched. 848 assertEquals(0, medium.size()); 849 850 // Verify the extras went to low priority. 851 assertEquals(2, low.size()); 852 assertEquals(5, low.get(0).eventId); 853 assertEquals(4, low.get(1).eventId); 854 } 855 856 @SmallTest 857 public void testRedistributeBuckets_tooManyMediumPriority() throws Exception { 858 ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>(); 859 ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>(); 860 ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>(); 861 high.add(createNotificationInfo(5)); 862 high.add(createNotificationInfo(4)); 863 medium.add(createNotificationInfo(3)); 864 medium.add(createNotificationInfo(2)); 865 medium.add(createNotificationInfo(1)); 866 867 // Invoke the method under test. 868 int maxNotifications = 3; 869 AlertService.redistributeBuckets(high, medium, low, maxNotifications); 870 871 // Verify high priority untouched. 872 assertEquals(2, high.size()); 873 assertEquals(5, high.get(0).eventId); 874 assertEquals(4, high.get(1).eventId); 875 876 // Verify some medium priority were kicked out (the ones near the end of the 877 // list). 878 assertEquals(1, medium.size()); 879 assertEquals(3, medium.get(0).eventId); 880 881 // Verify the extras went to low priority. 882 assertEquals(2, low.size()); 883 assertEquals(2, low.get(0).eventId); 884 assertEquals(1, low.get(1).eventId); 885 } 886 887 @SmallTest 888 public void testRedistributeBuckets_tooManyHighMediumPriority() throws Exception { 889 ArrayList<NotificationInfo> high = new ArrayList<NotificationInfo>(); 890 ArrayList<NotificationInfo> medium = new ArrayList<NotificationInfo>(); 891 ArrayList<NotificationInfo> low = new ArrayList<NotificationInfo>(); 892 high.add(createNotificationInfo(8)); 893 high.add(createNotificationInfo(7)); 894 high.add(createNotificationInfo(6)); 895 high.add(createNotificationInfo(5)); 896 high.add(createNotificationInfo(4)); 897 medium.add(createNotificationInfo(3)); 898 medium.add(createNotificationInfo(2)); 899 medium.add(createNotificationInfo(1)); 900 901 // Invoke the method under test. 902 int maxNotifications = 3; 903 AlertService.redistributeBuckets(high, medium, low, maxNotifications); 904 905 // Verify high priority. 906 assertEquals(3, high.size()); 907 assertEquals(6, high.get(0).eventId); 908 assertEquals(5, high.get(1).eventId); 909 assertEquals(4, high.get(2).eventId); 910 911 // Verify some medium priority. 912 assertEquals(0, medium.size()); 913 914 // Verify low priority. 915 assertEquals(5, low.size()); 916 assertEquals(8, low.get(0).eventId); 917 assertEquals(7, low.get(1).eventId); 918 assertEquals(3, low.get(2).eventId); 919 assertEquals(2, low.get(3).eventId); 920 assertEquals(1, low.get(4).eventId); 921 } 922} 923