1/* 2 * Copyright (C) 2010 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.event; 18 19import android.content.ContentProvider; 20import android.content.ContentProviderOperation; 21import android.content.ContentProviderResult; 22import android.content.ContentValues; 23import android.content.res.Resources; 24import android.database.Cursor; 25import android.database.MatrixCursor; 26import android.net.Uri; 27import android.provider.CalendarContract.Attendees; 28import android.provider.CalendarContract.Events; 29import android.provider.CalendarContract.Reminders; 30import android.test.AndroidTestCase; 31import android.test.mock.MockResources; 32import android.test.suitebuilder.annotation.SmallTest; 33import android.test.suitebuilder.annotation.Smoke; 34import android.text.TextUtils; 35import android.text.format.DateUtils; 36import android.text.format.Time; 37import android.text.util.Rfc822Token; 38import android.util.Log; 39 40import com.android.calendar.AbstractCalendarActivity; 41import com.android.calendar.AsyncQueryService; 42import com.android.calendar.CalendarEventModel; 43import com.android.calendar.CalendarEventModel.ReminderEntry; 44import com.android.calendar.R; 45import com.android.common.Rfc822Validator; 46 47import java.util.ArrayList; 48import java.util.Calendar; 49import java.util.LinkedHashSet; 50import java.util.TimeZone; 51 52public class EditEventHelperTest extends AndroidTestCase { 53 private static final int TEST_EVENT_ID = 1; 54 private static final int TEST_EVENT_INDEX_ID = 0; 55 private static final long TEST_END = 1272931200000L; 56 private static long TEST_END2 = 1272956400000L; 57 private static final long TEST_START = 1272844800000L; 58 private static long TEST_START2 = 1272870000000L; 59 private static final String LOCAL_TZ = TimeZone.getDefault().getID(); 60 61 private static final int SAVE_EVENT_NEW_EVENT = 1; 62 private static final int SAVE_EVENT_MOD_RECUR = 2; 63 private static final int SAVE_EVENT_RECUR_TO_NORECUR = 3; 64 private static final int SAVE_EVENT_NORECUR_TO_RECUR= 4; 65 private static final int SAVE_EVENT_MOD_NORECUR = 5; 66 private static final int SAVE_EVENT_MOD_INSTANCE = 6; 67 private static final int SAVE_EVENT_ALLFOLLOW_TO_NORECUR = 7; 68 private static final int SAVE_EVENT_FIRST_TO_NORECUR = 8; 69 private static final int SAVE_EVENT_FIRST_TO_RECUR = 9; 70 private static final int SAVE_EVENT_ALLFOLLOW_TO_RECUR = 10; 71 72 private static String[] TEST_CURSOR_DATA = new String[] { 73 Integer.toString(TEST_EVENT_ID), // 0 _id 74 "The Question", // 1 title 75 "Evaluating Life, the Universe, and Everything",// 2 description 76 "Earth Mk2", // 3 location 77 "1", // 4 All Day 78 "0", // 5 Has alarm 79 "2", // 6 Calendar id 80 "1272844800000", // 7 dtstart, Monday, May 3rd midnight UTC 81 "1272931200000", // 8 dtend, Tuesday, May 4th midnight UTC 82 "P3652421990D", // 9 duration, (10 million years) 83 "UTC", // 10 event timezone 84 "FREQ=DAILY;WKST=SU", // 11 rrule 85 "unique per calendar stuff", // 12 sync id 86 "0", // 13 transparency 87 "3", // 14 visibility 88 "steve@gmail.com", // 15 owner account 89 "1", // 16 has attendee data 90 null, //17 originalSyncId 91 "organizer@gmail.com", // 18 organizer 92 "0", // 19 guest can modify 93 null, // 20 original id 94 "1" // event status 95 }; // These should match up with EditEventHelper.EVENT_PROJECTION 96 97 private static final String AUTHORITY_URI = "content://EditEventHelperAuthority/"; 98 private static final String AUTHORITY = "EditEventHelperAuthority"; 99 100 private static final String TEST_ADDRESSES = 101 "no good, ad1@email.com, \"First Last\" <first@email.com> (comment), " + 102 "one.two.three@email.grue"; 103 private static final String TEST_ADDRESSES2 = 104 "no good, ad1@email.com, \"First Last\" <first@email.com> (comment), " + 105 "different@email.bit"; 106 107 108 private static final String TAG = "EEHTest"; 109 110 private CalendarEventModel mModel1; 111 private CalendarEventModel mModel2; 112 113 private ContentValues mValues; 114 private ContentValues mExpectedValues; 115 116 private EditEventHelper mHelper; 117 private AbstractCalendarActivity mActivity; 118 private int mCurrentSaveTest = 0; 119 120 @Override 121 public void setUp() { 122 Time time = new Time(Time.TIMEZONE_UTC); 123 time.set(TEST_START); 124 time.timezone = LOCAL_TZ; 125 TEST_START2 = time.normalize(true); 126 127 time.timezone = Time.TIMEZONE_UTC; 128 time.set(TEST_END); 129 time.timezone = LOCAL_TZ; 130 TEST_END2 = time.normalize(true); 131 } 132 133 private class MockAbsCalendarActivity extends AbstractCalendarActivity { 134 @Override 135 public AsyncQueryService getAsyncQueryService() { 136 if (mService == null) { 137 mService = new AsyncQueryService(this) { 138 @Override 139 public void startBatch(int token, Object cookie, String authority, 140 ArrayList<ContentProviderOperation> cpo, long delayMillis) { 141 mockApplyBatch(authority, cpo); 142 } 143 }; 144 } 145 return mService; 146 } 147 148 @Override 149 public Resources getResources() { 150 Resources res = new MockResources() { 151 @Override 152 // The actual selects singular vs plural as well and in the given language 153 public String getQuantityString(int id, int quantity) { 154 if (id == R.plurals.Nmins) { 155 return quantity + " mins"; 156 } 157 if (id == R.plurals.Nminutes) { 158 return quantity + " minutes"; 159 } 160 if (id == R.plurals.Nhours) { 161 return quantity + " hours"; 162 } 163 if (id == R.plurals.Ndays) { 164 return quantity + " days"; 165 } 166 return id + " " + quantity; 167 } 168 }; 169 return res; 170 } 171 } 172 173 private AbstractCalendarActivity buildTestContext() { 174 MockAbsCalendarActivity context = new MockAbsCalendarActivity(); 175 return context; 176 } 177 178 private ContentProviderResult[] mockApplyBatch(String authority, 179 ArrayList<ContentProviderOperation> operations) { 180 switch (mCurrentSaveTest) { 181 case SAVE_EVENT_NEW_EVENT: 182 // new recurring event 183 verifySaveEventNewEvent(operations); 184 break; 185 case SAVE_EVENT_MOD_RECUR: 186 // update to recurring event 187 verifySaveEventModifyRecurring(operations); 188 break; 189 case SAVE_EVENT_RECUR_TO_NORECUR: 190 // replace recurring event with non-recurring event 191 verifySaveEventRecurringToNonRecurring(operations); 192 break; 193 case SAVE_EVENT_NORECUR_TO_RECUR: 194 // update non-recurring event with recurring event 195 verifySaveEventNonRecurringToRecurring(operations); 196 break; 197 case SAVE_EVENT_MOD_NORECUR: 198 // update to non-recurring 199 verifySaveEventUpdateNonRecurring(operations); 200 break; 201 case SAVE_EVENT_MOD_INSTANCE: 202 // update to single instance of recurring event 203 verifySaveEventModifySingleInstance(operations); 204 break; 205 case SAVE_EVENT_ALLFOLLOW_TO_NORECUR: 206 // update all following with non-recurring event 207 verifySaveEventModifyAllFollowingWithNonRecurring(operations); 208 break; 209 case SAVE_EVENT_FIRST_TO_NORECUR: 210 // update all following with non-recurring event on first event in series 211 verifySaveEventModifyAllFollowingFirstWithNonRecurring(operations); 212 break; 213 case SAVE_EVENT_FIRST_TO_RECUR: 214 // update all following with recurring event on first event in series 215 verifySaveEventModifyAllFollowingFirstWithRecurring(operations); 216 break; 217 case SAVE_EVENT_ALLFOLLOW_TO_RECUR: 218 // update all following with recurring event on second event in series 219 verifySaveEventModifyAllFollowingWithRecurring(operations); 220 break; 221 } 222 return new ContentProviderResult[] {new ContentProviderResult(5)}; 223 } 224 225 private void addOwnerAttendeeToOps(ArrayList<ContentProviderOperation> expectedOps, int id) { 226 addOwnerAttendee(); 227 ContentProviderOperation.Builder b; 228 b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI).withValues(mExpectedValues); 229 b.withValueBackReference(Reminders.EVENT_ID, id); 230 expectedOps.add(b.build()); 231 } 232 233 // Some tests set the time values to one day later, this does that move in the values 234 private void moveExpectedTimeValuesForwardOneDay() { 235 long dayInMs = EditEventHelper.DAY_IN_SECONDS*1000; 236 mExpectedValues.put(Events.DTSTART, TEST_START + dayInMs); 237 mExpectedValues.put(Events.DTEND, TEST_END + dayInMs); 238 } 239 240 // Duplicates the delete and add for changing a single email address 241 private void addAttendeeChangesOps(ArrayList<ContentProviderOperation> expectedOps) { 242 ContentProviderOperation.Builder b = 243 ContentProviderOperation.newDelete(Attendees.CONTENT_URI); 244 b.withSelection(EditEventHelper.ATTENDEES_DELETE_PREFIX + "?)", 245 new String[] {"one.two.three@email.grue"}); 246 expectedOps.add(b.build()); 247 248 mExpectedValues.clear(); 249 mExpectedValues.put(Attendees.ATTENDEE_NAME, (String)null); 250 mExpectedValues.put(Attendees.ATTENDEE_EMAIL, "different@email.bit"); 251 mExpectedValues.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE); 252 mExpectedValues.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 253 mExpectedValues.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE); 254 mExpectedValues.put(Attendees.EVENT_ID, TEST_EVENT_ID); 255 b = ContentProviderOperation 256 .newInsert(Attendees.CONTENT_URI) 257 .withValues(mExpectedValues); 258 expectedOps.add(b.build()); 259 } 260 261 // This is a commonly added set of values 262 private void addOwnerAttendee() { 263 mExpectedValues.clear(); 264 mExpectedValues.put(Attendees.ATTENDEE_EMAIL, mModel1.mOwnerAccount); 265 mExpectedValues.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ORGANIZER); 266 mExpectedValues.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 267 mExpectedValues.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_ACCEPTED); 268 } 269 270 /** Some tests add all the attendees to the db, the names and emails should match 271 * with {@link #TEST_ADDRESSES2} minus the 'no good' 272 */ 273 private void addTestAttendees(ArrayList<ContentProviderOperation> ops, 274 boolean newEvent, int id) { 275 ContentProviderOperation.Builder b; 276 mExpectedValues.clear(); 277 mExpectedValues.put(Attendees.ATTENDEE_NAME, (String)null); 278 mExpectedValues.put(Attendees.ATTENDEE_EMAIL, "ad1@email.com"); 279 mExpectedValues.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE); 280 mExpectedValues.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 281 mExpectedValues.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE); 282 283 if (newEvent) { 284 b = ContentProviderOperation 285 .newInsert(Attendees.CONTENT_URI) 286 .withValues(mExpectedValues); 287 b.withValueBackReference(Attendees.EVENT_ID, id); 288 } else { 289 mExpectedValues.put(Attendees.EVENT_ID, id); 290 b = ContentProviderOperation 291 .newInsert(Attendees.CONTENT_URI) 292 .withValues(mExpectedValues); 293 } 294 ops.add(b.build()); 295 296 mExpectedValues.clear(); 297 mExpectedValues.put(Attendees.ATTENDEE_NAME, "First Last"); 298 mExpectedValues.put(Attendees.ATTENDEE_EMAIL, "first@email.com"); 299 mExpectedValues.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE); 300 mExpectedValues.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 301 mExpectedValues.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE); 302 303 if (newEvent) { 304 b = ContentProviderOperation 305 .newInsert(Attendees.CONTENT_URI) 306 .withValues(mExpectedValues); 307 b.withValueBackReference(Attendees.EVENT_ID, id); 308 } else { 309 mExpectedValues.put(Attendees.EVENT_ID, id); 310 b = ContentProviderOperation 311 .newInsert(Attendees.CONTENT_URI) 312 .withValues(mExpectedValues); 313 } 314 ops.add(b.build()); 315 316 mExpectedValues.clear(); 317 mExpectedValues.put(Attendees.ATTENDEE_NAME, (String)null); 318 mExpectedValues.put(Attendees.ATTENDEE_EMAIL, "different@email.bit"); 319 mExpectedValues.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE); 320 mExpectedValues.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 321 mExpectedValues.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE); 322 323 if (newEvent) { 324 b = ContentProviderOperation 325 .newInsert(Attendees.CONTENT_URI) 326 .withValues(mExpectedValues); 327 b.withValueBackReference(Attendees.EVENT_ID, id); 328 } else { 329 mExpectedValues.put(Attendees.EVENT_ID, id); 330 b = ContentProviderOperation 331 .newInsert(Attendees.CONTENT_URI) 332 .withValues(mExpectedValues); 333 } 334 ops.add(b.build()); 335 } 336 337 @Smoke 338 @SmallTest 339 public void testSaveEventFailures() { 340 mActivity = buildTestContext(); 341 mHelper = new EditEventHelper(mActivity, null); 342 343 mModel1 = buildTestModel(); 344 mModel2 = buildTestModel(); 345 346 // saveEvent should return false early if: 347 // -it was set to not ok 348 // -the model was null 349 // -the event doesn't represent the same event as the original event 350 // -there's a uri but an original event is not provided 351 mHelper.mEventOk = false; 352 assertFalse(mHelper.saveEvent(null, null, 0)); 353 mHelper.mEventOk = true; 354 assertFalse(mHelper.saveEvent(null, null, 0)); 355 mModel2.mId = 13; 356 assertFalse(mHelper.saveEvent(mModel1, mModel2, 0)); 357 mModel2.mId = mModel1.mId; 358 mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 359 mModel2.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 360 assertFalse(mHelper.saveEvent(mModel1, null, 0)); 361 } 362 363 @Smoke 364 @SmallTest 365 public void testSaveEventNewEvent() { 366 // Creates a model of a new event for saving 367 mActivity = buildTestContext(); 368 mHelper = new EditEventHelper(mActivity, null); 369 370 mModel1 = buildTestModel(); 371// mModel1.mAttendees = TEST_ADDRESSES2; 372 mCurrentSaveTest = SAVE_EVENT_NEW_EVENT; 373 374 assertTrue(mHelper.saveEvent(mModel1, null, 0)); 375 } 376 377 private boolean verifySaveEventNewEvent(ArrayList<ContentProviderOperation> ops) { 378 ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); 379 int br_id = 0; 380 mExpectedValues = buildTestValues(); 381 mExpectedValues.put(Events.HAS_ALARM, 0); 382 mExpectedValues.put(Events.HAS_ATTENDEE_DATA, 1); 383 ContentProviderOperation.Builder b = ContentProviderOperation 384 .newInsert(Events.CONTENT_URI) 385 .withValues(mExpectedValues); 386 expectedOps.add(b.build()); 387 388 // This call has a separate unit test so we'll use it to simplify making the expected vals 389 mHelper.saveRemindersWithBackRef(expectedOps, br_id, mModel1.mReminders, 390 new ArrayList<ReminderEntry>(), true); 391 392 addOwnerAttendeeToOps(expectedOps, br_id); 393 394 addTestAttendees(expectedOps, true, br_id); 395 396 assertEquals(ops, expectedOps); 397 return true; 398 } 399 400 @Smoke 401 @SmallTest 402 public void testSaveEventModifyRecurring() { 403 // Creates an original and an updated recurring event model 404 mActivity = buildTestContext(); 405 mHelper = new EditEventHelper(mActivity, null); 406 407 mModel1 = buildTestModel(); 408 mModel2 = buildTestModel(); 409// mModel1.mAttendees = TEST_ADDRESSES2; 410 411 // Updating a recurring event with a new attendee list 412 mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 413 // And a new start time to ensure the time fields aren't removed 414 mModel1.mOriginalStart = TEST_START; 415 416 // The original model is assumed correct so drop the no good bit 417// mModel2.mAttendees = "ad1@email.com, \"First Last\" <first@email.com> (comment), " + 418// "one.two.three@email.grue"; 419 mCurrentSaveTest = SAVE_EVENT_MOD_RECUR; 420 421 assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL)); 422 } 423 424 private boolean verifySaveEventModifyRecurring(ArrayList<ContentProviderOperation> ops) { 425 ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); 426 int br_id = 0; 427 mExpectedValues = buildTestValues(); 428 mExpectedValues.put(Events.HAS_ALARM, 0); 429 // This is tested elsewhere, used for convenience here 430 mHelper.checkTimeDependentFields(mModel2, mModel1, mExpectedValues, 431 EditEventHelper.MODIFY_ALL); 432 433 expectedOps.add(ContentProviderOperation.newUpdate(Uri.parse(mModel1.mUri)).withValues( 434 mExpectedValues).build()); 435 436 // This call has a separate unit test so we'll use it to simplify making the expected vals 437 mHelper.saveReminders(expectedOps, TEST_EVENT_ID, mModel1.mReminders, 438 mModel2.mReminders, false); 439 440 addAttendeeChangesOps(expectedOps); 441 442 assertEquals(ops, expectedOps); 443 return true; 444 } 445 446 @Smoke 447 @SmallTest 448 public void testSaveEventRecurringToNonRecurring() { 449 // Creates an original and an updated recurring event model 450 mActivity = buildTestContext(); 451 mHelper = new EditEventHelper(mActivity, null); 452 453 mModel1 = buildTestModel(); 454 mModel2 = buildTestModel(); 455// mModel1.mAttendees = TEST_ADDRESSES2; 456 457 // Updating a recurring event with a new attendee list 458 mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 459 // And a new start time to ensure the time fields aren't removed 460 mModel1.mOriginalStart = TEST_START; 461 462 // The original model is assumed correct so drop the no good bit 463// mModel2.mAttendees = "ad1@email.com, \"First Last\" <first@email.com> (comment), " + 464// "one.two.three@email.grue"; 465 466 // Replace an existing recurring event with a non-recurring event 467 mModel1.mRrule = null; 468 mModel1.mEnd = TEST_END; 469 mCurrentSaveTest = SAVE_EVENT_RECUR_TO_NORECUR; 470 471 assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL)); 472 } 473 474 private boolean verifySaveEventRecurringToNonRecurring(ArrayList<ContentProviderOperation> ops) 475 { 476 ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); 477 int id = 0; 478 mExpectedValues = buildNonRecurringTestValues(); 479 mExpectedValues.put(Events.HAS_ALARM, 0); 480 // This is tested elsewhere, used for convenience here 481 mHelper.checkTimeDependentFields(mModel1, mModel1, mExpectedValues, 482 EditEventHelper.MODIFY_ALL); 483 484 expectedOps.add(ContentProviderOperation.newDelete(Uri.parse(mModel1.mUri)).build()); 485 id = expectedOps.size(); 486 expectedOps.add(ContentProviderOperation 487 .newInsert(Events.CONTENT_URI) 488 .withValues(mExpectedValues) 489 .build()); 490 491 mHelper.saveRemindersWithBackRef(expectedOps, id, mModel1.mReminders, 492 mModel2.mReminders, true); 493 494 addOwnerAttendeeToOps(expectedOps, id); 495 496 addTestAttendees(expectedOps, true, id); 497 498 assertEquals(ops, expectedOps); 499 return true; 500 } 501 502 @Smoke 503 @SmallTest 504 public void testSaveEventNonRecurringToRecurring() { 505 // Creates an original non-recurring and an updated recurring event model 506 mActivity = buildTestContext(); 507 mHelper = new EditEventHelper(mActivity, null); 508 509 mModel1 = buildTestModel(); 510 mModel2 = buildTestModel(); 511// mModel1.mAttendees = TEST_ADDRESSES2; 512 513 // Updating a recurring event with a new attendee list 514 mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 515 // And a new start time to ensure the time fields aren't removed 516 mModel1.mOriginalStart = TEST_START; 517 518 // The original model is assumed correct so drop the no good bit 519// mModel2.mAttendees = "ad1@email.com, \"First Last\" <first@email.com> (comment), " + 520// "one.two.three@email.grue"; 521 522 mModel2.mRrule = null; 523 mModel2.mEnd = TEST_END; 524 mCurrentSaveTest = SAVE_EVENT_NORECUR_TO_RECUR; 525 526 assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL)); 527 } 528 529 private boolean verifySaveEventNonRecurringToRecurring(ArrayList<ContentProviderOperation> ops) 530 { 531 // Changing a non-recurring event to a recurring event should generate the same operations 532 // as just modifying a recurring event. 533 return verifySaveEventModifyRecurring(ops); 534 } 535 536 @Smoke 537 @SmallTest 538 public void testSaveEventUpdateNonRecurring() { 539 // Creates an original non-recurring and an updated recurring event model 540 mActivity = buildTestContext(); 541 mHelper = new EditEventHelper(mActivity, null); 542 543 mModel1 = buildTestModel(); 544 mModel2 = buildTestModel(); 545// mModel1.mAttendees = TEST_ADDRESSES2; 546 547 // Updating a recurring event with a new attendee list 548 mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 549 // And a new start time to ensure the time fields aren't removed 550 mModel1.mOriginalStart = TEST_START; 551 552 // The original model is assumed correct so drop the no good bit 553// mModel2.mAttendees = "ad1@email.com, \"First Last\" <first@email.com> (comment), " + 554// "one.two.three@email.grue"; 555 556 mModel2.mRrule = null; 557 mModel2.mEnd = TEST_END2; 558 mModel1.mRrule = null; 559 mModel1.mEnd = TEST_END2; 560 mCurrentSaveTest = SAVE_EVENT_MOD_NORECUR; 561 562 assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL)); 563 } 564 565 private boolean verifySaveEventUpdateNonRecurring(ArrayList<ContentProviderOperation> ops) { 566 ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); 567 int id = TEST_EVENT_ID; 568 mExpectedValues = buildNonRecurringTestValues(); 569 mExpectedValues.put(Events.HAS_ALARM, 0); 570 // This is tested elsewhere, used for convenience here 571 mHelper.checkTimeDependentFields(mModel1, mModel1, mExpectedValues, 572 EditEventHelper.MODIFY_ALL); 573 expectedOps.add(ContentProviderOperation.newUpdate(Uri.parse(mModel1.mUri)).withValues( 574 mExpectedValues).build()); 575 // This call has a separate unit test so we'll use it to simplify making the expected vals 576 mHelper.saveReminders(expectedOps, TEST_EVENT_ID, mModel1.mReminders, 577 mModel2.mReminders, false); 578 addAttendeeChangesOps(expectedOps); 579 580 assertEquals(ops, expectedOps); 581 return true; 582 } 583 584 @Smoke 585 @SmallTest 586 public void testSaveEventModifySingleInstance() { 587 // Creates an original non-recurring and an updated recurring event model 588 mActivity = buildTestContext(); 589 mHelper = new EditEventHelper(mActivity, null); 590 591 mModel1 = buildTestModel(); 592 mModel2 = buildTestModel(); 593// mModel1.mAttendees = TEST_ADDRESSES2; 594 595 mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 596 // And a new start time to ensure the time fields aren't removed 597 mModel1.mOriginalStart = TEST_START; 598 599 // The original model is assumed correct so drop the no good bit 600// mModel2.mAttendees = "ad1@email.com, \"First Last\" <first@email.com> (comment), " + 601// "one.two.three@email.grue"; 602 603 // Modify the second instance of the event 604 long dayInMs = EditEventHelper.DAY_IN_SECONDS*1000; 605 mModel1.mRrule = null; 606 mModel1.mEnd = TEST_END + dayInMs; 607 mModel1.mStart += dayInMs; 608 mModel1.mOriginalStart = mModel1.mStart; 609 610 mCurrentSaveTest = SAVE_EVENT_MOD_INSTANCE; 611 // Only modify this instance 612 assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_SELECTED)); 613 } 614 615 private boolean verifySaveEventModifySingleInstance(ArrayList<ContentProviderOperation> ops) { 616 ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); 617 int id = 0; 618 mExpectedValues = buildNonRecurringTestValues(); 619 mExpectedValues.put(Events.HAS_ALARM, 0); 620 // This is tested elsewhere, used for convenience here 621 mHelper.checkTimeDependentFields(mModel1, mModel1, mExpectedValues, 622 EditEventHelper.MODIFY_ALL); 623 624 moveExpectedTimeValuesForwardOneDay(); 625 mExpectedValues.put(Events.ORIGINAL_SYNC_ID, mModel2.mSyncId); 626 mExpectedValues.put(Events.ORIGINAL_INSTANCE_TIME, mModel1.mOriginalStart); 627 mExpectedValues.put(Events.ORIGINAL_ALL_DAY, 1); 628 629 ContentProviderOperation.Builder b = ContentProviderOperation 630 .newInsert(Events.CONTENT_URI) 631 .withValues(mExpectedValues); 632 expectedOps.add(b.build()); 633 634 mHelper.saveRemindersWithBackRef(expectedOps, id, mModel1.mReminders, 635 mModel2.mReminders, true); 636 637 addOwnerAttendeeToOps(expectedOps, id); 638 639 addTestAttendees(expectedOps, true, id); 640 641 assertEquals(ops, expectedOps); 642 return true; 643 } 644 645 @Smoke 646 @SmallTest 647 public void testSaveEventModifyAllFollowingWithNonRecurring() { 648 // Creates an original and an updated recurring event model. The update starts on the 2nd 649 // instance of the original. 650 mActivity = buildTestContext(); 651 mHelper = new EditEventHelper(mActivity, null); 652 653 mModel1 = buildTestModel(); 654 mModel2 = buildTestModel(); 655// mModel1.mAttendees = TEST_ADDRESSES2; 656 657 mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 658 mModel2.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 659 660 // The original model is assumed correct so drop the no good bit 661// mModel2.mAttendees = "ad1@email.com, \"First Last\" <first@email.com> (comment), " + 662// "one.two.three@email.grue"; 663 664 // Modify the second instance of the event 665 long dayInMs = EditEventHelper.DAY_IN_SECONDS*1000; 666 mModel1.mRrule = null; 667 mModel1.mEnd = TEST_END + dayInMs; 668 mModel1.mStart += dayInMs; 669 mModel1.mOriginalStart = mModel1.mStart; 670 671 mCurrentSaveTest = SAVE_EVENT_ALLFOLLOW_TO_NORECUR; 672 // Only modify this instance 673 assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL_FOLLOWING)); 674 } 675 676 private boolean verifySaveEventModifyAllFollowingWithNonRecurring( 677 ArrayList<ContentProviderOperation> ops) { 678 ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); 679 int id = 0; 680 mExpectedValues = buildNonRecurringTestValues(); 681 mExpectedValues.put(Events.HAS_ALARM, 0); 682 moveExpectedTimeValuesForwardOneDay(); 683 // This has a separate test 684 mHelper.updatePastEvents(expectedOps, mModel2, mModel1.mOriginalStart); 685 id = expectedOps.size(); 686 expectedOps.add(ContentProviderOperation 687 .newInsert(Events.CONTENT_URI) 688 .withValues(mExpectedValues) 689 .build()); 690 691 mHelper.saveRemindersWithBackRef(expectedOps, id, mModel1.mReminders, 692 mModel2.mReminders, true); 693 694 addOwnerAttendeeToOps(expectedOps, id); 695 696 addTestAttendees(expectedOps, true, id); 697 698 assertEquals(ops, expectedOps); 699 return true; 700 } 701 702 @Smoke 703 @SmallTest 704 public void testSaveEventModifyAllFollowingFirstWithNonRecurring() { 705 // Creates an original recurring and an updated non-recurring event model for the first 706 // instance. This should replace the original event with a non-recurring event. 707 mActivity = buildTestContext(); 708 mHelper = new EditEventHelper(mActivity, null); 709 710 mModel1 = buildTestModel(); 711 mModel2 = buildTestModel(); 712// mModel1.mAttendees = TEST_ADDRESSES2; 713 714 mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 715 mModel2.mUri = mModel1.mUri; 716 // And a new start time to ensure the time fields aren't removed 717 mModel1.mOriginalStart = TEST_START; 718 719 // The original model is assumed correct so drop the no good bit 720// mModel2.mAttendees = "ad1@email.com, \"First Last\" <first@email.com> (comment), " + 721// "one.two.three@email.grue"; 722 723 // Move the event one day but keep original start set to the first instance 724 long dayInMs = EditEventHelper.DAY_IN_SECONDS*1000; 725 mModel1.mRrule = null; 726 mModel1.mEnd = TEST_END + dayInMs; 727 mModel1.mStart += dayInMs; 728 729 mCurrentSaveTest = SAVE_EVENT_FIRST_TO_NORECUR; 730 // Only modify this instance 731 assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL_FOLLOWING)); 732 } 733 734 private boolean verifySaveEventModifyAllFollowingFirstWithNonRecurring( 735 ArrayList<ContentProviderOperation> ops) { 736 737 ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); 738 int id = 0; 739 mExpectedValues = buildNonRecurringTestValues(); 740 mExpectedValues.put(Events.HAS_ALARM, 0); 741 moveExpectedTimeValuesForwardOneDay(); 742 743 expectedOps.add(ContentProviderOperation.newDelete(Uri.parse(mModel1.mUri)).build()); 744 id = expectedOps.size(); 745 expectedOps.add(ContentProviderOperation 746 .newInsert(Events.CONTENT_URI) 747 .withValues(mExpectedValues) 748 .build()); 749 750 mHelper.saveRemindersWithBackRef(expectedOps, id, mModel1.mReminders, 751 mModel2.mReminders, true); 752 753 addOwnerAttendeeToOps(expectedOps, id); 754 755 addTestAttendees(expectedOps, true, id); 756 757 assertEquals(ops, expectedOps); 758 return true; 759 } 760 761 @Smoke 762 @SmallTest 763 public void testSaveEventModifyAllFollowingFirstWithRecurring() { 764 // Creates an original recurring and an updated recurring event model for the first instance 765 // This should replace the original event with a new recurrence 766 mActivity = buildTestContext(); 767 mHelper = new EditEventHelper(mActivity, null); 768 769 mModel1 = buildTestModel(); 770 mModel2 = buildTestModel(); 771// mModel1.mAttendees = TEST_ADDRESSES2; 772 773 mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 774 mModel2.mUri = mModel1.mUri; 775 // And a new start time to ensure the time fields aren't removed 776 mModel1.mOriginalStart = TEST_START; 777 778 // The original model is assumed correct so drop the no good bit 779// mModel2.mAttendees = "ad1@email.com, \"First Last\" <first@email.com> (comment), " + 780// "one.two.three@email.grue"; 781 782 // Move the event one day but keep original start set to the first instance 783 long dayInMs = EditEventHelper.DAY_IN_SECONDS*1000; 784 mModel1.mStart += dayInMs; 785 786 mCurrentSaveTest = SAVE_EVENT_FIRST_TO_RECUR; 787 // Only modify this instance 788 assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL_FOLLOWING)); 789 } 790 791 private boolean verifySaveEventModifyAllFollowingFirstWithRecurring( 792 ArrayList<ContentProviderOperation> ops) { 793 ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); 794 int br_id = 0; 795 mExpectedValues = buildTestValues(); 796 mExpectedValues.put(Events.HAS_ALARM, 0); 797 moveExpectedTimeValuesForwardOneDay(); 798 mExpectedValues.put(Events.DTEND, (Long)null); 799 // This is tested elsewhere, used for convenience here 800 mHelper.checkTimeDependentFields(mModel2, mModel1, mExpectedValues, 801 EditEventHelper.MODIFY_ALL_FOLLOWING); 802 803 expectedOps.add(ContentProviderOperation.newUpdate(Uri.parse(mModel1.mUri)).withValues( 804 mExpectedValues).build()); 805 806 // This call has a separate unit test so we'll use it to simplify making the expected vals 807 mHelper.saveReminders(expectedOps, TEST_EVENT_ID, mModel1.mReminders, 808 mModel2.mReminders, true); 809 810 addAttendeeChangesOps(expectedOps); 811 812 assertEquals(ops, expectedOps); 813 return true; 814 } 815 816 @Smoke 817 @SmallTest 818 public void testSaveEventModifyAllFollowingWithRecurring() { 819 // Creates an original recurring and an updated recurring event model 820 // for the second instance. This should end the original recurrence and add a new 821 // recurrence. 822 mActivity = buildTestContext(); 823 mHelper = new EditEventHelper(mActivity, null); 824 825 mModel1 = buildTestModel(); 826 mModel2 = buildTestModel(); 827// mModel1.mAttendees = TEST_ADDRESSES2; 828 829 mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 830 mModel2.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 831 832 // The original model is assumed correct so drop the no good bit 833// mModel2.mAttendees = "ad1@email.com, \"First Last\" <first@email.com> (comment), " + 834// "one.two.three@email.grue"; 835 836 // Move the event one day and the original start so it references the second instance 837 long dayInMs = EditEventHelper.DAY_IN_SECONDS*1000; 838 mModel1.mStart += dayInMs; 839 mModel1.mOriginalStart = mModel1.mStart; 840 841 mCurrentSaveTest = SAVE_EVENT_ALLFOLLOW_TO_RECUR; 842 // Only modify this instance 843 assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL_FOLLOWING)); 844 } 845 846 private boolean verifySaveEventModifyAllFollowingWithRecurring( 847 ArrayList<ContentProviderOperation> ops) { 848 ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); 849 int br_id = 0; 850 mExpectedValues = buildTestValues(); 851 mExpectedValues.put(Events.HAS_ALARM, 0); 852 moveExpectedTimeValuesForwardOneDay(); 853 mExpectedValues.put(Events.DTEND, (Long)null); 854 // This is tested elsewhere, used for convenience here 855 mHelper.updatePastEvents(expectedOps, mModel2, mModel1.mOriginalStart); 856 857 br_id = expectedOps.size(); 858 expectedOps.add(ContentProviderOperation 859 .newInsert(Events.CONTENT_URI) 860 .withValues(mExpectedValues) 861 .build()); 862 863 // This call has a separate unit test so we'll use it to simplify making the expected vals 864 mHelper.saveRemindersWithBackRef(expectedOps, br_id, mModel1.mReminders, 865 mModel2.mReminders, true); 866 867 addOwnerAttendeeToOps(expectedOps, br_id); 868 869 addTestAttendees(expectedOps, true, br_id); 870 871 assertEquals(ops, expectedOps); 872 return true; 873 } 874 875 @Smoke 876 @SmallTest 877 public void testGetAddressesFromList() { 878 mActivity = buildTestContext(); 879 mHelper = new EditEventHelper(mActivity, null); 880 881 LinkedHashSet<Rfc822Token> expected = new LinkedHashSet<Rfc822Token>(); 882 expected.add(new Rfc822Token(null, "ad1@email.com", "")); 883 expected.add(new Rfc822Token("First Last", "first@email.com", "comment")); 884 expected.add(new Rfc822Token(null, "one.two.three@email.grue", "")); 885 886 LinkedHashSet<Rfc822Token> actual = mHelper.getAddressesFromList(TEST_ADDRESSES, 887 new Rfc822Validator(null)); 888 assertEquals(expected, actual); 889 } 890 891 @Smoke 892 @SmallTest 893 public void testConstructDefaultStartTime() { 894 mActivity = buildTestContext(); 895 mHelper = new EditEventHelper(mActivity, null); 896 897 long now = 0; 898 long expected = now + 30 * DateUtils.MINUTE_IN_MILLIS; 899 assertEquals(expected, mHelper.constructDefaultStartTime(now)); 900 901 // 2:00 -> 2:30 902 now = 1262340000000L; // Fri Jan 01 2010 02:00:00 GMT-0800 (PST) 903 expected = now + 30 * DateUtils.MINUTE_IN_MILLIS; 904 assertEquals(expected, mHelper.constructDefaultStartTime(now)); 905 906 // 2:01 -> 2:30 907 now += DateUtils.MINUTE_IN_MILLIS; 908 assertEquals(expected, mHelper.constructDefaultStartTime(now)); 909 910 // 2:02 -> 2:30 911 now += DateUtils.MINUTE_IN_MILLIS; 912 assertEquals(expected, mHelper.constructDefaultStartTime(now)); 913 914 // 2:32 -> 3:00 915 now += 30 * DateUtils.MINUTE_IN_MILLIS; 916 expected += 30 * DateUtils.MINUTE_IN_MILLIS; 917 assertEquals(expected, mHelper.constructDefaultStartTime(now)); 918 919 // 2:33 -> 3:00 920 now += DateUtils.MINUTE_IN_MILLIS; 921 assertEquals(expected, mHelper.constructDefaultStartTime(now)); 922 923 } 924 925 @Smoke 926 @SmallTest 927 public void testConstructDefaultEndTime() { 928 mActivity = buildTestContext(); 929 mHelper = new EditEventHelper(mActivity, null); 930 931 long start = 1262340000000L; 932 long expected = start + DateUtils.HOUR_IN_MILLIS; 933 assertEquals(expected, mHelper.constructDefaultEndTime(start)); 934 } 935 936 @Smoke 937 @SmallTest 938 public void testCheckTimeDependentFieldsNoChanges() { 939 mActivity = buildTestContext(); 940 mHelper = new EditEventHelper(mActivity, null); 941 942 mModel1 = buildTestModel(); 943 mModel2 = buildTestModel(); 944 mModel2.mRrule = null; 945 946 mValues = buildTestValues(); 947 mExpectedValues = buildTestValues(); 948 949 // if any time/recurrence vals are different but there's no new rrule it 950 // shouldn't change 951 mHelper.checkTimeDependentFields(mModel1, mModel2, mValues, EditEventHelper.MODIFY_ALL); 952 assertEquals(mValues, mExpectedValues); 953 954 // also, if vals are different and it's not modifying all it shouldn't 955 // change. 956 mModel2.mRrule = "something else"; 957 mHelper.checkTimeDependentFields(mModel1, mModel2, mValues, 958 EditEventHelper.MODIFY_SELECTED); 959 assertEquals(mValues, mExpectedValues); 960 961 // if vals changed and modify all is selected dtstart should be updated 962 // by the difference 963 // between originalStart and start 964 mModel2.mOriginalStart = mModel2.mStart + 60000; // set the old time to 965 // one minute later 966 mModel2.mStart += 120000; // move the event another 1 minute. 967 968 // shouldn't change for an allday event 969 // expectedVals.put(Events.DTSTART, mModel1.mStart + 60000); // should 970 // now be 1 minute later 971 // dtstart2 shouldn't change since it gets rezeroed in the local 972 // timezone for allDay events 973 974 mHelper.checkTimeDependentFields(mModel1, mModel2, mValues, 975 EditEventHelper.MODIFY_SELECTED); 976 assertEquals(mValues, mExpectedValues); 977 } 978 979 @Smoke 980 @SmallTest 981 public void testCheckTimeDependentFieldsChanges() { 982 mActivity = buildTestContext(); 983 mHelper = new EditEventHelper(mActivity, null); 984 985 mModel1 = buildTestModel(); 986 mModel2 = buildTestModel(); 987 mModel2.mRrule = null; 988 989 mValues = buildTestValues(); 990 mExpectedValues = buildTestValues(); 991 992 // if all the time values are the same it should remove them from vals 993 mModel2.mRrule = mModel1.mRrule; 994 mModel2.mStart = mModel1.mStart; 995 mModel2.mOriginalStart = mModel2.mStart; 996 997 mExpectedValues.remove(Events.DTSTART); 998 mExpectedValues.remove(Events.DTEND); 999 mExpectedValues.remove(Events.DURATION); 1000 mExpectedValues.remove(Events.ALL_DAY); 1001 mExpectedValues.remove(Events.RRULE); 1002 mExpectedValues.remove(Events.EVENT_TIMEZONE); 1003 1004 mHelper.checkTimeDependentFields(mModel1, mModel2, mValues, 1005 EditEventHelper.MODIFY_SELECTED); 1006 assertEquals(mValues, mExpectedValues); 1007 1008 } 1009 1010 @Smoke 1011 @SmallTest 1012 public void testUpdatePastEvents() { 1013 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 1014 ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); 1015 long initialBeginTime = 1472864400000L; // Sep 3, 2016, 12AM UTC time 1016 mValues = new ContentValues(); 1017 1018 mModel1 = buildTestModel(); 1019 mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); 1020 mActivity = buildTestContext(); 1021 mHelper = new EditEventHelper(mActivity, null); 1022 1023 mValues.put(Events.RRULE, "FREQ=DAILY;UNTIL=20160903;WKST=SU"); // yyyymmddThhmmssZ 1024 mValues.put(Events.DTSTART, TEST_START); 1025 1026 ContentProviderOperation.Builder b = ContentProviderOperation.newUpdate( 1027 Uri.parse(mModel1.mUri)).withValues(mValues); 1028 expectedOps.add(b.build()); 1029 1030 mHelper.updatePastEvents(ops, mModel1, initialBeginTime); 1031 assertEquals(ops, expectedOps); 1032 1033 mModel1.mAllDay = false; 1034 1035 mValues.put(Events.RRULE, "FREQ=DAILY;UNTIL=20160903T005959Z;WKST=SU"); // yyyymmddThhmmssZ 1036 1037 expectedOps.clear(); 1038 b = ContentProviderOperation.newUpdate(Uri.parse(mModel1.mUri)).withValues(mValues); 1039 expectedOps.add(b.build()); 1040 1041 ops.clear(); 1042 mHelper.updatePastEvents(ops, mModel1, initialBeginTime); 1043 assertEquals(ops, expectedOps); 1044 } 1045 1046 @Smoke 1047 @SmallTest 1048 public void testConstructReminderLabel() { 1049 mActivity = buildTestContext(); 1050 1051 String label = EventViewUtils.constructReminderLabel(mActivity, 35, true); 1052 assertEquals(label, "35 mins"); 1053 1054 label = EventViewUtils.constructReminderLabel(mActivity, 72, false); 1055 assertEquals(label, "72 minutes"); 1056 1057 label = EventViewUtils.constructReminderLabel(mActivity, 60, true); 1058 assertEquals(label, "1 hours"); 1059 1060 label = EventViewUtils.constructReminderLabel(mActivity, 60 * 48, true); 1061 assertEquals(label, "2 days"); 1062 } 1063 1064 @Smoke 1065 @SmallTest 1066 public void testIsSameEvent() { 1067 mModel1 = new CalendarEventModel(); 1068 mModel2 = new CalendarEventModel(); 1069 1070 mModel1.mId = 1; 1071 mModel1.mCalendarId = 1; 1072 mModel2.mId = 1; 1073 mModel2.mCalendarId = 1; 1074 1075 // considered the same if the event and calendar ids both match 1076 assertTrue(EditEventHelper.isSameEvent(mModel1, mModel2)); 1077 1078 mModel2.mId = 2; 1079 assertFalse(EditEventHelper.isSameEvent(mModel1, mModel2)); 1080 1081 mModel2.mId = 1; 1082 mModel2.mCalendarId = 2; 1083 assertFalse(EditEventHelper.isSameEvent(mModel1, mModel2)); 1084 } 1085 1086 @Smoke 1087 @SmallTest 1088 public void testSaveReminders() { 1089 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 1090 ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); 1091 long eventId = TEST_EVENT_ID; 1092 ArrayList<ReminderEntry> reminders = new ArrayList<ReminderEntry>(); 1093 ArrayList<ReminderEntry> originalReminders = new ArrayList<ReminderEntry>(); 1094 boolean forceSave = true; 1095 boolean result; 1096 mActivity = buildTestContext(); 1097 mHelper = new EditEventHelper(mActivity, null); 1098 assertNotNull(mHelper); 1099 1100 // First test forcing a delete with no reminders. 1101 String where = Reminders.EVENT_ID + "=?"; 1102 String[] args = new String[] {Long.toString(eventId)}; 1103 ContentProviderOperation.Builder b = 1104 ContentProviderOperation.newDelete(Reminders.CONTENT_URI); 1105 b.withSelection(where, args); 1106 expectedOps.add(b.build()); 1107 1108 result = mHelper.saveReminders(ops, eventId, reminders, originalReminders, forceSave); 1109 assertTrue(result); 1110 assertEquals(expectedOps, ops); 1111 1112 // Now test calling save with identical reminders and no forcing 1113 reminders.add(ReminderEntry.valueOf(5)); 1114 reminders.add(ReminderEntry.valueOf(10)); 1115 reminders.add(ReminderEntry.valueOf(15)); 1116 1117 originalReminders.add(ReminderEntry.valueOf(5)); 1118 originalReminders.add(ReminderEntry.valueOf(10)); 1119 originalReminders.add(ReminderEntry.valueOf(15)); 1120 1121 forceSave = false; 1122 1123 ops.clear(); 1124 1125 // Should fail to create any ops since nothing changed 1126 result = mHelper.saveReminders(ops, eventId, reminders, originalReminders, forceSave); 1127 assertFalse(result); 1128 assertEquals(ops.size(), 0); 1129 1130 //Now test adding a single reminder 1131 originalReminders.remove(2); 1132 1133 addExpectedMinutes(expectedOps); 1134 1135 result = mHelper.saveReminders(ops, eventId, reminders, originalReminders, forceSave); 1136 assertTrue(result); 1137 assertEquals(expectedOps, ops); 1138 } 1139 1140 @Smoke 1141 @SmallTest 1142 public void testSaveRemindersWithBackRef() { 1143 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 1144 ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); 1145 long eventId = TEST_EVENT_ID; 1146 ArrayList<ReminderEntry> reminders = new ArrayList<ReminderEntry>(); 1147 ArrayList<ReminderEntry> originalReminders = new ArrayList<ReminderEntry>(); 1148 boolean forceSave = true; 1149 boolean result; 1150 mActivity = buildTestContext(); 1151 mHelper = new EditEventHelper(mActivity, null); 1152 assertNotNull(mHelper); 1153 1154 // First test forcing a delete with no reminders. 1155 ContentProviderOperation.Builder b = 1156 ContentProviderOperation.newDelete(Reminders.CONTENT_URI); 1157 b.withSelection(Reminders.EVENT_ID + "=?", new String[1]); 1158 b.withSelectionBackReference(0, TEST_EVENT_INDEX_ID); 1159 expectedOps.add(b.build()); 1160 1161 result = 1162 mHelper.saveRemindersWithBackRef(ops, TEST_EVENT_INDEX_ID, reminders, 1163 originalReminders, forceSave); 1164 assertTrue(result); 1165 assertEquals(ops, expectedOps); 1166 1167 // Now test calling save with identical reminders and no forcing 1168 reminders.add(ReminderEntry.valueOf(5)); 1169 reminders.add(ReminderEntry.valueOf(10)); 1170 reminders.add(ReminderEntry.valueOf(15)); 1171 1172 originalReminders.add(ReminderEntry.valueOf(5)); 1173 originalReminders.add(ReminderEntry.valueOf(10)); 1174 originalReminders.add(ReminderEntry.valueOf(15)); 1175 1176 forceSave = false; 1177 1178 ops.clear(); 1179 1180 result = mHelper.saveRemindersWithBackRef(ops, ops.size(), reminders, originalReminders, 1181 forceSave); 1182 assertFalse(result); 1183 assertEquals(ops.size(), 0); 1184 1185 //Now test adding a single reminder 1186 originalReminders.remove(2); 1187 1188 addExpectedMinutesWithBackRef(expectedOps); 1189 1190 result = mHelper.saveRemindersWithBackRef(ops, ops.size(), reminders, originalReminders, 1191 forceSave); 1192 assertTrue(result); 1193 assertEquals(ops, expectedOps); 1194 } 1195 1196 @Smoke 1197 @SmallTest 1198 public void testIsFirstEventInSeries() { 1199 mModel1 = new CalendarEventModel(); 1200 mModel2 = new CalendarEventModel(); 1201 1202 // It's considered the first event if the original start of the new model matches the 1203 // start of the old model 1204 mModel1.mOriginalStart = 100; 1205 mModel1.mStart = 200; 1206 mModel2.mOriginalStart = 100; 1207 mModel2.mStart = 100; 1208 1209 assertTrue(EditEventHelper.isFirstEventInSeries(mModel1, mModel2)); 1210 1211 mModel1.mOriginalStart = 80; 1212 assertFalse(EditEventHelper.isFirstEventInSeries(mModel1, mModel2)); 1213 } 1214 1215 @Smoke 1216 @SmallTest 1217 public void testAddRecurrenceRule() { 1218 mActivity = buildTestContext(); 1219 mHelper = new EditEventHelper(mActivity, null); 1220 mValues = new ContentValues(); 1221 mExpectedValues = new ContentValues(); 1222 mModel1 = new CalendarEventModel(); 1223 1224 mExpectedValues.put(Events.RRULE, "Weekly, Monday"); 1225 mExpectedValues.put(Events.DURATION, "P60S"); 1226 mExpectedValues.put(Events.DTEND, (Long) null); 1227 1228 mModel1.mRrule = "Weekly, Monday"; 1229 mModel1.mStart = 1; 1230 mModel1.mEnd = 60001; 1231 mModel1.mAllDay = false; 1232 1233 mHelper.addRecurrenceRule(mValues, mModel1); 1234 assertEquals(mValues, mExpectedValues); 1235 1236 mExpectedValues.put(Events.DURATION, "P1D"); 1237 1238 mModel1.mAllDay = true; 1239 mValues.clear(); 1240 1241 mHelper.addRecurrenceRule(mValues, mModel1); 1242 assertEquals(mValues, mExpectedValues); 1243 1244 } 1245 1246 @Smoke 1247 @SmallTest 1248 public void testUpdateRecurrenceRule() { 1249 int selection = EditEventHelper.DOES_NOT_REPEAT; 1250 int weekStart = Calendar.SUNDAY; 1251 mModel1 = new CalendarEventModel(); 1252 mModel1.mTimezone = Time.TIMEZONE_UTC; 1253 mModel1.mStart = 1272665741000L; // Fri, April 30th ~ 3:17PM 1254 1255 mModel1.mRrule = "This should go away"; 1256 1257 EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); 1258 assertNull(mModel1.mRrule); 1259 1260 mModel1.mRrule = "This shouldn't change"; 1261 selection = EditEventHelper.REPEATS_CUSTOM; 1262 1263 EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); 1264 assertEquals(mModel1.mRrule, "This shouldn't change"); 1265 1266 selection = EditEventHelper.REPEATS_DAILY; 1267 1268 EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); 1269 assertEquals(mModel1.mRrule, "FREQ=DAILY;WKST=SU"); 1270 1271 selection = EditEventHelper.REPEATS_EVERY_WEEKDAY; 1272 1273 EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); 1274 assertEquals(mModel1.mRrule, "FREQ=WEEKLY;WKST=SU;BYDAY=MO,TU,WE,TH,FR"); 1275 1276 selection = EditEventHelper.REPEATS_WEEKLY_ON_DAY; 1277 1278 EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); 1279 assertEquals(mModel1.mRrule, "FREQ=WEEKLY;WKST=SU;BYDAY=FR"); 1280 1281 selection = EditEventHelper.REPEATS_MONTHLY_ON_DAY; 1282 1283 EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); 1284 assertEquals(mModel1.mRrule, "FREQ=MONTHLY;WKST=SU;BYMONTHDAY=30"); 1285 1286 selection = EditEventHelper.REPEATS_MONTHLY_ON_DAY_COUNT; 1287 1288 EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); 1289 assertEquals(mModel1.mRrule, "FREQ=MONTHLY;WKST=SU;BYDAY=-1FR"); 1290 1291 selection = EditEventHelper.REPEATS_YEARLY; 1292 1293 EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); 1294 assertEquals(mModel1.mRrule, "FREQ=YEARLY;WKST=SU"); 1295 } 1296 1297 @Smoke 1298 @SmallTest 1299 public void testSetModelFromCursor() { 1300 mActivity = buildTestContext(); 1301 mHelper = new EditEventHelper(mActivity, null); 1302 MatrixCursor c = new MatrixCursor(EditEventHelper.EVENT_PROJECTION); 1303 c.addRow(TEST_CURSOR_DATA); 1304 1305 mModel1 = new CalendarEventModel(); 1306 mModel2 = buildTestModel(); 1307 1308 EditEventHelper.setModelFromCursor(mModel1, c); 1309 assertEquals(mModel1, mModel2); 1310 1311 TEST_CURSOR_DATA[EditEventHelper.EVENT_INDEX_ALL_DAY] = "0"; 1312 c.close(); 1313 c = new MatrixCursor(EditEventHelper.EVENT_PROJECTION); 1314 c.addRow(TEST_CURSOR_DATA); 1315 1316 mModel2.mAllDay = false; 1317 mModel2.mStart = TEST_START; // UTC time 1318 1319 EditEventHelper.setModelFromCursor(mModel1, c); 1320 assertEquals(mModel1, mModel2); 1321 1322 TEST_CURSOR_DATA[EditEventHelper.EVENT_INDEX_RRULE] = null; 1323 c.close(); 1324 c = new MatrixCursor(EditEventHelper.EVENT_PROJECTION); 1325 c.addRow(TEST_CURSOR_DATA); 1326 1327 mModel2.mRrule = null; 1328 mModel2.mEnd = TEST_END; 1329 mModel2.mDuration = null; 1330 1331 EditEventHelper.setModelFromCursor(mModel1, c); 1332 assertEquals(mModel1, mModel2); 1333 1334 TEST_CURSOR_DATA[EditEventHelper.EVENT_INDEX_ALL_DAY] = "1"; 1335 c.close(); 1336 c = new MatrixCursor(EditEventHelper.EVENT_PROJECTION); 1337 c.addRow(TEST_CURSOR_DATA); 1338 1339 mModel2.mAllDay = true; 1340 mModel2.mStart = TEST_START; // Monday, May 3rd, midnight 1341 mModel2.mEnd = TEST_END; // Tuesday, May 4th, midnight 1342 1343 EditEventHelper.setModelFromCursor(mModel1, c); 1344 assertEquals(mModel1, mModel2); 1345 } 1346 1347 @Smoke 1348 @SmallTest 1349 public void testGetContentValuesFromModel() { 1350 mActivity = buildTestContext(); 1351 mHelper = new EditEventHelper(mActivity, null); 1352 mExpectedValues = buildTestValues(); 1353 mModel1 = buildTestModel(); 1354 1355 ContentValues values = mHelper.getContentValuesFromModel(mModel1); 1356 assertEquals(values, mExpectedValues); 1357 1358 mModel1.mRrule = null; 1359 mModel1.mEnd = TEST_END; 1360 1361 mExpectedValues.put(Events.RRULE, (String) null); 1362 mExpectedValues.put(Events.DURATION, (String) null); 1363 mExpectedValues.put(Events.DTEND, TEST_END); // UTC time 1364 1365 values = mHelper.getContentValuesFromModel(mModel1); 1366 assertEquals(values, mExpectedValues); 1367 1368 mModel1.mAllDay = false; 1369 1370 mExpectedValues.put(Events.ALL_DAY, 0); 1371 mExpectedValues.put(Events.DTSTART, TEST_START); 1372 mExpectedValues.put(Events.DTEND, TEST_END); 1373 // not an allday event so timezone isn't modified 1374 mExpectedValues.put(Events.EVENT_TIMEZONE, "UTC"); 1375 1376 values = mHelper.getContentValuesFromModel(mModel1); 1377 assertEquals(values, mExpectedValues); 1378 } 1379 1380 @Smoke 1381 @SmallTest 1382 public void testExtractDomain() { 1383 String domain = EditEventHelper.extractDomain("test.email@gmail.com"); 1384 assertEquals(domain, "gmail.com"); 1385 1386 domain = EditEventHelper.extractDomain("bademail.no#$%at symbol"); 1387 assertNull(domain); 1388 } 1389 1390 private void addExpectedMinutes(ArrayList<ContentProviderOperation> expectedOps) { 1391 ContentProviderOperation.Builder b; 1392 mValues = new ContentValues(); 1393 1394 mValues.clear(); 1395 mValues.put(Reminders.MINUTES, 5); 1396 mValues.put(Reminders.METHOD, Reminders.METHOD_DEFAULT); 1397 mValues.put(Reminders.EVENT_ID, TEST_EVENT_ID); 1398 b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(mValues); 1399 expectedOps.add(b.build()); 1400 1401 mValues.clear(); 1402 mValues.put(Reminders.MINUTES, 10); 1403 mValues.put(Reminders.METHOD, Reminders.METHOD_DEFAULT); 1404 mValues.put(Reminders.EVENT_ID, TEST_EVENT_ID); 1405 b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(mValues); 1406 expectedOps.add(b.build()); 1407 1408 mValues.clear(); 1409 mValues.put(Reminders.MINUTES, 15); 1410 mValues.put(Reminders.METHOD, Reminders.METHOD_DEFAULT); 1411 mValues.put(Reminders.EVENT_ID, TEST_EVENT_ID); 1412 b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(mValues); 1413 expectedOps.add(b.build()); 1414 } 1415 1416 private void addExpectedMinutesWithBackRef(ArrayList<ContentProviderOperation> expectedOps) { 1417 ContentProviderOperation.Builder b; 1418 mValues = new ContentValues(); 1419 1420 mValues.clear(); 1421 mValues.put(Reminders.MINUTES, 5); 1422 mValues.put(Reminders.METHOD, Reminders.METHOD_DEFAULT); 1423 b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(mValues); 1424 b.withValueBackReference(Reminders.EVENT_ID, TEST_EVENT_INDEX_ID); 1425 expectedOps.add(b.build()); 1426 1427 mValues.clear(); 1428 mValues.put(Reminders.MINUTES, 10); 1429 mValues.put(Reminders.METHOD, Reminders.METHOD_DEFAULT); 1430 b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(mValues); 1431 b.withValueBackReference(Reminders.EVENT_ID, TEST_EVENT_INDEX_ID); 1432 expectedOps.add(b.build()); 1433 1434 mValues.clear(); 1435 mValues.put(Reminders.MINUTES, 15); 1436 mValues.put(Reminders.METHOD, Reminders.METHOD_DEFAULT); 1437 b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(mValues); 1438 b.withValueBackReference(Reminders.EVENT_ID, TEST_EVENT_INDEX_ID); 1439 expectedOps.add(b.build()); 1440 } 1441 1442 private static void assertEquals(ArrayList<ContentProviderOperation> expected, 1443 ArrayList<ContentProviderOperation> actual) { 1444 if (expected == null) { 1445 assertNull(actual); 1446 } 1447 int size = expected.size(); 1448 1449 assertEquals(size, actual.size()); 1450 1451 for (int i = 0; i < size; i++) { 1452 assertTrue("At index " + i + ", expected:\n" + String.valueOf(expected) + 1453 "\nActual:\n" + String.valueOf(actual), 1454 cpoEquals(expected.get(i), actual.get(i))); 1455 } 1456 1457 } 1458 1459 private static boolean cpoEquals(ContentProviderOperation cpo1, ContentProviderOperation cpo2) { 1460 if (cpo1 == null && cpo2 != null) { 1461 return false; 1462 } 1463 if (cpo1 == cpo2) { 1464 return true; 1465 } 1466 if (cpo2 == null) { 1467 return false; 1468 } 1469 1470 return TextUtils.equals(cpo1.toString(), cpo2.toString()); 1471 } 1472 1473 // Generates a default model for testing. Should match up with 1474 // generateTestValues 1475 private CalendarEventModel buildTestModel() { 1476 CalendarEventModel model = new CalendarEventModel(); 1477 model.mId = TEST_EVENT_ID; 1478 model.mTitle = "The Question"; 1479 model.mDescription = "Evaluating Life, the Universe, and Everything"; 1480 model.mLocation = "Earth Mk2"; 1481 model.mAllDay = true; 1482 model.mHasAlarm = false; 1483 model.mCalendarId = 2; 1484 model.mStart = TEST_START; // Monday, May 3rd, local Time 1485 model.mDuration = "P3652421990D"; 1486 // The model uses the local timezone for allday 1487 model.mTimezone = "UTC"; 1488 model.mRrule = "FREQ=DAILY;WKST=SU"; 1489 model.mSyncId = "unique per calendar stuff"; 1490 model.mAvailability = 0; 1491 model.mAccessLevel = 2; // This is one less than the values written if >0 1492 model.mOwnerAccount = "steve@gmail.com"; 1493 model.mHasAttendeeData = true; 1494 model.mEventStatus = Events.STATUS_CONFIRMED; 1495 model.mOrganizer = "organizer@gmail.com"; 1496 model.mGuestsCanModify = false; 1497 model.mModelUpdatedWithEventCursor = true; 1498 return model; 1499 } 1500 1501 // Generates a default set of values for testing. Should match up with 1502 // generateTestModel 1503 private ContentValues buildTestValues() { 1504 ContentValues values = new ContentValues(); 1505 1506 values.put(Events.CALENDAR_ID, 2L); 1507 values.put(Events.EVENT_TIMEZONE, "UTC"); // Allday events are converted 1508 // to UTC for the db 1509 values.put(Events.TITLE, "The Question"); 1510 values.put(Events.ALL_DAY, 1); 1511 values.put(Events.DTSTART, TEST_START); // Monday, May 3rd, midnight UTC time 1512 values.put(Events.HAS_ATTENDEE_DATA, 1); 1513 1514 values.put(Events.RRULE, "FREQ=DAILY;WKST=SU"); 1515 values.put(Events.DURATION, "P3652421990D"); 1516 values.put(Events.DTEND, (Long) null); 1517 values.put(Events.DESCRIPTION, "Evaluating Life, the Universe, and Everything"); 1518 values.put(Events.EVENT_LOCATION, "Earth Mk2"); 1519 values.put(Events.AVAILABILITY, 0); 1520 values.put(Events.STATUS, Events.STATUS_CONFIRMED); 1521 values.put(Events.ACCESS_LEVEL, 3); // This is one more than the model if 1522 // >0 1523 1524 return values; 1525 } 1526 1527 private ContentValues buildNonRecurringTestValues() { 1528 ContentValues values = buildTestValues(); 1529 values.put(Events.DURATION, (String)null); 1530 values.put(Events.DTEND, TEST_END); 1531 values.put(Events.RRULE, (String)null); 1532 return values; 1533 } 1534 1535 // This gets called by EditEventHelper to read or write the data 1536 class TestProvider extends ContentProvider { 1537 int index = 0; 1538 1539 public TestProvider() { 1540 } 1541 1542 @Override 1543 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 1544 String orderBy) { 1545 return null; 1546 } 1547 1548 @Override 1549 public int delete(Uri uri, String selection, String[] selectionArgs) { 1550 return 0; 1551 } 1552 1553 @Override 1554 public String getType(Uri uri) { 1555 return null; 1556 } 1557 1558 @Override 1559 public boolean onCreate() { 1560 return false; 1561 } 1562 1563 @Override 1564 public Uri insert(Uri uri, ContentValues values) { 1565 // TODO Auto-generated method stub 1566 return null; 1567 } 1568 1569 @Override 1570 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 1571 // TODO Auto-generated method stub 1572 return 0; 1573 } 1574 } 1575 1576} 1577