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