Home | History | Annotate | Download | only in event
      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