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