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