| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package com.android.calendar.event; |
| |
| import android.content.ContentProvider; |
| import android.content.ContentProviderOperation; |
| import android.content.ContentProviderResult; |
| import android.content.ContentValues; |
| import android.content.res.Resources; |
| import android.database.Cursor; |
| import android.database.MatrixCursor; |
| import android.net.Uri; |
| import android.provider.CalendarContract.Attendees; |
| import android.provider.CalendarContract.Events; |
| import android.provider.CalendarContract.Reminders; |
| import android.test.AndroidTestCase; |
| import android.test.mock.MockResources; |
| import android.test.suitebuilder.annotation.SmallTest; |
| import android.test.suitebuilder.annotation.Smoke; |
| import android.text.format.DateUtils; |
| import android.text.format.Time; |
| import android.text.util.Rfc822Token; |
| |
| import com.android.calendar.AbstractCalendarActivity; |
| import com.android.calendar.AsyncQueryService; |
| import com.android.calendar.CalendarEventModel; |
| import com.android.calendar.CalendarEventModel.ReminderEntry; |
| import com.android.calendar.R; |
| import com.android.calendar.Utils; |
| import com.android.common.Rfc822Validator; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Calendar; |
| import java.util.LinkedHashSet; |
| import java.util.TimeZone; |
| |
| public class EditEventHelperTest extends AndroidTestCase { |
| private static final int TEST_EVENT_ID = 1; |
| private static final int TEST_EVENT_INDEX_ID = 0; |
| private static final long TEST_END = 1272931200000L; |
| private static long TEST_END2 = 1272956400000L; |
| private static final long TEST_START = 1272844800000L; |
| private static long TEST_START2 = 1272870000000L; |
| private static final String LOCAL_TZ = TimeZone.getDefault().getID(); |
| |
| private static final int SAVE_EVENT_NEW_EVENT = 1; |
| private static final int SAVE_EVENT_MOD_RECUR = 2; |
| private static final int SAVE_EVENT_RECUR_TO_NORECUR = 3; |
| private static final int SAVE_EVENT_NORECUR_TO_RECUR= 4; |
| private static final int SAVE_EVENT_MOD_NORECUR = 5; |
| private static final int SAVE_EVENT_MOD_INSTANCE = 6; |
| private static final int SAVE_EVENT_ALLFOLLOW_TO_NORECUR = 7; |
| private static final int SAVE_EVENT_FIRST_TO_NORECUR = 8; |
| private static final int SAVE_EVENT_FIRST_TO_RECUR = 9; |
| private static final int SAVE_EVENT_ALLFOLLOW_TO_RECUR = 10; |
| |
| /* These should match up with EditEventHelper.EVENT_PROJECTION. |
| * Note that spaces and commas have been removed to allow for easier sanitation. |
| */ |
| private static String[] TEST_CURSOR_DATA = new String[] { |
| Integer.toString(TEST_EVENT_ID), // 0 _id |
| "The_Question", // 1 title |
| "Evaluating_Life_the_Universe_and_Everything", // 2 description |
| "Earth_Mk2", // 3 location |
| "1", // 4 All Day |
| "0", // 5 Has alarm |
| "2", // 6 Calendar id |
| "1272844800000", // 7 dtstart, Monday, May 3rd midnight UTC |
| "1272931200000", // 8 dtend, Tuesday, May 4th midnight UTC |
| "P3652421990D", // 9 duration, (10 million years) |
| "UTC", // 10 event timezone |
| "FREQ=DAILY;WKST=SU", // 11 rrule |
| "unique_per_calendar_stuff", // 12 sync id |
| "0", // 13 transparency/availability |
| "3", // 14 visibility/access level |
| "steve@gmail.com", // 15 owner account |
| "1", // 16 has attendee data |
| null, //17 originalSyncId |
| "organizer@gmail.com", // 18 organizer |
| "0", // 19 guest can modify |
| "-1", // 20 original id |
| "1", // 21 event status |
| "-339611", // 22 calendar color |
| "-2350809", // 23 event color |
| "11" // 24 event color key |
| }; |
| |
| private static final String AUTHORITY_URI = "content://EditEventHelperAuthority/"; |
| private static final String AUTHORITY = "EditEventHelperAuthority"; |
| |
| private static final String TEST_ADDRESSES = |
| "no good, ad1@email.com, \"First Last\" <first@email.com> (comment), " + |
| "one.two.three@email.grue"; |
| private static final String TEST_ADDRESSES2 = |
| "no good, ad1@email.com, \"First Last\" <first@email.com> (comment), " + |
| "different@email.bit"; |
| private static final String TEST_ADDRESSES3 = |
| "ad1@email.com, \"First Last\" <first@email.com> (comment), " + |
| "different@email.bit"; |
| private static final String TEST_ADDRESSES4 = |
| "ad1@email.com, \"First Last\" <first@email.com> (comment), " + |
| "one.two.three@email.grue"; |
| |
| |
| private static final String TAG = "EEHTest"; |
| |
| private Rfc822Validator mEmailValidator; |
| private CalendarEventModel mModel1; |
| private CalendarEventModel mModel2; |
| |
| private ContentValues mValues; |
| private ContentValues mExpectedValues; |
| |
| private EditEventHelper mHelper; |
| private AbstractCalendarActivity mActivity; |
| private int mCurrentSaveTest = 0; |
| |
| @Override |
| public void setUp() { |
| Time time = new Time(Time.TIMEZONE_UTC); |
| time.set(TEST_START); |
| time.timezone = LOCAL_TZ; |
| TEST_START2 = time.normalize(true); |
| |
| time.timezone = Time.TIMEZONE_UTC; |
| time.set(TEST_END); |
| time.timezone = LOCAL_TZ; |
| TEST_END2 = time.normalize(true); |
| |
| mEmailValidator = new Rfc822Validator(null); |
| } |
| |
| private class MockAbsCalendarActivity extends AbstractCalendarActivity { |
| @Override |
| public AsyncQueryService getAsyncQueryService() { |
| if (mService == null) { |
| mService = new AsyncQueryService(this) { |
| @Override |
| public void startBatch(int token, Object cookie, String authority, |
| ArrayList<ContentProviderOperation> cpo, long delayMillis) { |
| mockApplyBatch(authority, cpo); |
| } |
| }; |
| } |
| return mService; |
| } |
| |
| @Override |
| public Resources getResources() { |
| Resources res = new MockResources() { |
| @Override |
| // The actual selects singular vs plural as well and in the given language |
| public String getQuantityString(int id, int quantity) { |
| if (id == R.plurals.Nmins) { |
| return quantity + " mins"; |
| } |
| if (id == R.plurals.Nminutes) { |
| return quantity + " minutes"; |
| } |
| if (id == R.plurals.Nhours) { |
| return quantity + " hours"; |
| } |
| if (id == R.plurals.Ndays) { |
| return quantity + " days"; |
| } |
| return id + " " + quantity; |
| } |
| }; |
| return res; |
| } |
| } |
| |
| private AbstractCalendarActivity buildTestContext() { |
| MockAbsCalendarActivity context = new MockAbsCalendarActivity(); |
| return context; |
| } |
| |
| private ContentProviderResult[] mockApplyBatch(String authority, |
| ArrayList<ContentProviderOperation> operations) { |
| switch (mCurrentSaveTest) { |
| case SAVE_EVENT_NEW_EVENT: |
| // new recurring event |
| verifySaveEventNewEvent(operations); |
| break; |
| case SAVE_EVENT_MOD_RECUR: |
| // update to recurring event |
| verifySaveEventModifyRecurring(operations); |
| break; |
| case SAVE_EVENT_RECUR_TO_NORECUR: |
| // replace recurring event with non-recurring event |
| verifySaveEventRecurringToNonRecurring(operations); |
| break; |
| case SAVE_EVENT_NORECUR_TO_RECUR: |
| // update non-recurring event with recurring event |
| verifySaveEventNonRecurringToRecurring(operations); |
| break; |
| case SAVE_EVENT_MOD_NORECUR: |
| // update to non-recurring |
| verifySaveEventUpdateNonRecurring(operations); |
| break; |
| case SAVE_EVENT_MOD_INSTANCE: |
| // update to single instance of recurring event |
| verifySaveEventModifySingleInstance(operations); |
| break; |
| case SAVE_EVENT_ALLFOLLOW_TO_NORECUR: |
| // update all following with non-recurring event |
| verifySaveEventModifyAllFollowingWithNonRecurring(operations); |
| break; |
| case SAVE_EVENT_FIRST_TO_NORECUR: |
| // update all following with non-recurring event on first event in series |
| verifySaveEventModifyAllFollowingFirstWithNonRecurring(operations); |
| break; |
| case SAVE_EVENT_FIRST_TO_RECUR: |
| // update all following with recurring event on first event in series |
| verifySaveEventModifyAllFollowingFirstWithRecurring(operations); |
| break; |
| case SAVE_EVENT_ALLFOLLOW_TO_RECUR: |
| // update all following with recurring event on second event in series |
| verifySaveEventModifyAllFollowingWithRecurring(operations); |
| break; |
| } |
| return new ContentProviderResult[] {new ContentProviderResult(5)}; |
| } |
| |
| private void addOwnerAttendeeToOps(ArrayList<ContentProviderOperation> expectedOps, int id) { |
| addOwnerAttendee(); |
| ContentProviderOperation.Builder b; |
| b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI).withValues(mExpectedValues); |
| b.withValueBackReference(Reminders.EVENT_ID, id); |
| expectedOps.add(b.build()); |
| } |
| |
| private void addOwnerAttendeeToOps(ArrayList<ContentProviderOperation> expectedOps) { |
| addOwnerAttendee(); |
| mExpectedValues.put(Attendees.EVENT_ID, TEST_EVENT_ID); |
| ContentProviderOperation.Builder b; |
| b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI).withValues(mExpectedValues); |
| expectedOps.add(b.build()); |
| } |
| |
| |
| // Some tests set the time values to one day later, this does that move in the values |
| private void moveExpectedTimeValuesForwardOneDay() { |
| long dayInMs = EditEventHelper.DAY_IN_SECONDS*1000; |
| mExpectedValues.put(Events.DTSTART, TEST_START + dayInMs); |
| mExpectedValues.put(Events.DTEND, TEST_END + dayInMs); |
| } |
| |
| // Duplicates the delete and add for changing a single email address |
| private void addAttendeeChangesOps(ArrayList<ContentProviderOperation> expectedOps) { |
| ContentProviderOperation.Builder b = |
| ContentProviderOperation.newDelete(Attendees.CONTENT_URI); |
| b.withSelection(EditEventHelper.ATTENDEES_DELETE_PREFIX + "?)", |
| new String[] {"one.two.three@email.grue"}); |
| expectedOps.add(b.build()); |
| |
| mExpectedValues.clear(); |
| mExpectedValues.put(Attendees.ATTENDEE_NAME, "different@email.bit"); |
| mExpectedValues.put(Attendees.ATTENDEE_EMAIL, "different@email.bit"); |
| mExpectedValues.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE); |
| mExpectedValues.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED); |
| mExpectedValues.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE); |
| mExpectedValues.put(Attendees.EVENT_ID, TEST_EVENT_ID); |
| b = ContentProviderOperation |
| .newInsert(Attendees.CONTENT_URI) |
| .withValues(mExpectedValues); |
| expectedOps.add(b.build()); |
| } |
| |
| // This is a commonly added set of values |
| private void addOwnerAttendee() { |
| mExpectedValues.clear(); |
| mExpectedValues.put(Attendees.ATTENDEE_EMAIL, mModel1.mOwnerAccount); |
| mExpectedValues.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ORGANIZER); |
| mExpectedValues.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED); |
| mExpectedValues.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_ACCEPTED); |
| } |
| |
| /** Some tests add all the attendees to the db, the names and emails should match |
| * with {@link #TEST_ADDRESSES2} minus the 'no good' |
| */ |
| private void addTestAttendees(ArrayList<ContentProviderOperation> ops, |
| boolean newEvent, int id) { |
| ContentProviderOperation.Builder b; |
| mExpectedValues.clear(); |
| mExpectedValues.put(Attendees.ATTENDEE_NAME, "ad1@email.com"); |
| mExpectedValues.put(Attendees.ATTENDEE_EMAIL, "ad1@email.com"); |
| mExpectedValues.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE); |
| mExpectedValues.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED); |
| mExpectedValues.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE); |
| |
| if (newEvent) { |
| b = ContentProviderOperation |
| .newInsert(Attendees.CONTENT_URI) |
| .withValues(mExpectedValues); |
| b.withValueBackReference(Attendees.EVENT_ID, id); |
| } else { |
| mExpectedValues.put(Attendees.EVENT_ID, id); |
| b = ContentProviderOperation |
| .newInsert(Attendees.CONTENT_URI) |
| .withValues(mExpectedValues); |
| } |
| ops.add(b.build()); |
| |
| mExpectedValues.clear(); |
| mExpectedValues.put(Attendees.ATTENDEE_NAME, "First Last"); |
| mExpectedValues.put(Attendees.ATTENDEE_EMAIL, "first@email.com"); |
| mExpectedValues.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE); |
| mExpectedValues.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED); |
| mExpectedValues.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE); |
| |
| if (newEvent) { |
| b = ContentProviderOperation |
| .newInsert(Attendees.CONTENT_URI) |
| .withValues(mExpectedValues); |
| b.withValueBackReference(Attendees.EVENT_ID, id); |
| } else { |
| mExpectedValues.put(Attendees.EVENT_ID, id); |
| b = ContentProviderOperation |
| .newInsert(Attendees.CONTENT_URI) |
| .withValues(mExpectedValues); |
| } |
| ops.add(b.build()); |
| |
| mExpectedValues.clear(); |
| mExpectedValues.put(Attendees.ATTENDEE_NAME, "different@email.bit"); |
| mExpectedValues.put(Attendees.ATTENDEE_EMAIL, "different@email.bit"); |
| mExpectedValues.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE); |
| mExpectedValues.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED); |
| mExpectedValues.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE); |
| |
| if (newEvent) { |
| b = ContentProviderOperation |
| .newInsert(Attendees.CONTENT_URI) |
| .withValues(mExpectedValues); |
| b.withValueBackReference(Attendees.EVENT_ID, id); |
| } else { |
| mExpectedValues.put(Attendees.EVENT_ID, id); |
| b = ContentProviderOperation |
| .newInsert(Attendees.CONTENT_URI) |
| .withValues(mExpectedValues); |
| } |
| ops.add(b.build()); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSaveEventFailures() { |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mModel1 = buildTestModel(); |
| mModel2 = buildTestModel(); |
| |
| // saveEvent should return false early if: |
| // -it was set to not ok |
| // -the model was null |
| // -the event doesn't represent the same event as the original event |
| // -there's a uri but an original event is not provided |
| mHelper.mEventOk = false; |
| assertFalse(mHelper.saveEvent(null, null, 0)); |
| mHelper.mEventOk = true; |
| assertFalse(mHelper.saveEvent(null, null, 0)); |
| mModel2.mId = 13; |
| assertFalse(mHelper.saveEvent(mModel1, mModel2, 0)); |
| mModel2.mId = mModel1.mId; |
| mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| mModel2.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| assertFalse(mHelper.saveEvent(mModel1, null, 0)); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSaveEventNewEvent() { |
| // Creates a model of a new event for saving |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mModel1 = buildTestModel(); |
| mModel1.addAttendees(TEST_ADDRESSES2, mEmailValidator); |
| mCurrentSaveTest = SAVE_EVENT_NEW_EVENT; |
| |
| assertTrue(mHelper.saveEvent(mModel1, null, 0)); |
| } |
| |
| private boolean verifySaveEventNewEvent(ArrayList<ContentProviderOperation> ops) { |
| ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); |
| int br_id = 0; |
| mExpectedValues = buildTestValues(); |
| mExpectedValues.put(Events.HAS_ALARM, 0); |
| mExpectedValues.put(Events.HAS_ATTENDEE_DATA, 1); |
| ContentProviderOperation.Builder b = ContentProviderOperation |
| .newInsert(Events.CONTENT_URI) |
| .withValues(mExpectedValues); |
| expectedOps.add(b.build()); |
| |
| // This call has a separate unit test so we'll use it to simplify making the expected vals |
| mHelper.saveRemindersWithBackRef(expectedOps, br_id, mModel1.mReminders, |
| new ArrayList<ReminderEntry>(), true); |
| |
| addOwnerAttendeeToOps(expectedOps, br_id); |
| |
| addTestAttendees(expectedOps, true, br_id); |
| |
| assertEquals(expectedOps, ops); |
| return true; |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSaveEventModifyRecurring() { |
| // Creates an original and an updated recurring event model |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mModel1 = buildTestModel(); |
| mModel2 = buildTestModel(); |
| mModel1.addAttendees(TEST_ADDRESSES2, mEmailValidator); |
| |
| // Updating a recurring event with a new attendee list |
| mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| // And a new start time to ensure the time fields aren't removed |
| mModel1.mOriginalStart = TEST_START; |
| |
| // The original model is assumed correct so drop the no good bit |
| mModel2.addAttendees(TEST_ADDRESSES4, null); |
| mCurrentSaveTest = SAVE_EVENT_MOD_RECUR; |
| |
| assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL)); |
| } |
| |
| private boolean verifySaveEventModifyRecurring(ArrayList<ContentProviderOperation> ops) { |
| ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); |
| int br_id = 0; |
| mExpectedValues = buildTestValues(); |
| mExpectedValues.put(Events.HAS_ALARM, 0); |
| // This is tested elsewhere, used for convenience here |
| mHelper.checkTimeDependentFields(mModel2, mModel1, mExpectedValues, |
| EditEventHelper.MODIFY_ALL); |
| |
| expectedOps.add(ContentProviderOperation.newUpdate(Uri.parse(mModel1.mUri)).withValues( |
| mExpectedValues).build()); |
| |
| // This call has a separate unit test so we'll use it to simplify making the expected vals |
| mHelper.saveReminders(expectedOps, TEST_EVENT_ID, mModel1.mReminders, |
| mModel2.mReminders, false); |
| |
| addOwnerAttendeeToOps(expectedOps); |
| addAttendeeChangesOps(expectedOps); |
| |
| assertEquals(expectedOps, ops); |
| return true; |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSaveEventRecurringToNonRecurring() { |
| // Creates an original and an updated recurring event model |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mModel1 = buildTestModel(); |
| mModel2 = buildTestModel(); |
| mModel1.addAttendees(TEST_ADDRESSES2, mEmailValidator); |
| |
| // Updating a recurring event with a new attendee list |
| mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| // And a new start time to ensure the time fields aren't removed |
| mModel1.mOriginalStart = TEST_START; |
| |
| // The original model is assumed correct so drop the no good bit |
| mModel2.addAttendees(TEST_ADDRESSES4, null); |
| |
| // Replace an existing recurring event with a non-recurring event |
| mModel1.mRrule = null; |
| mModel1.mEnd = TEST_END; |
| mCurrentSaveTest = SAVE_EVENT_RECUR_TO_NORECUR; |
| |
| assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL)); |
| } |
| |
| private boolean verifySaveEventRecurringToNonRecurring(ArrayList<ContentProviderOperation> ops) |
| { |
| ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); |
| int id = 0; |
| mExpectedValues = buildNonRecurringTestValues(); |
| mExpectedValues.put(Events.HAS_ALARM, 0); |
| // This is tested elsewhere, used for convenience here |
| mHelper.checkTimeDependentFields(mModel1, mModel1, mExpectedValues, |
| EditEventHelper.MODIFY_ALL); |
| |
| expectedOps.add(ContentProviderOperation.newDelete(Uri.parse(mModel1.mUri)).build()); |
| id = expectedOps.size(); |
| expectedOps.add(ContentProviderOperation |
| .newInsert(Events.CONTENT_URI) |
| .withValues(mExpectedValues) |
| .build()); |
| |
| mHelper.saveRemindersWithBackRef(expectedOps, id, mModel1.mReminders, |
| mModel2.mReminders, true); |
| |
| addOwnerAttendeeToOps(expectedOps, id); |
| |
| addTestAttendees(expectedOps, true, id); |
| |
| assertEquals(expectedOps, ops); |
| return true; |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSaveEventNonRecurringToRecurring() { |
| // Creates an original non-recurring and an updated recurring event model |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mModel1 = buildTestModel(); |
| mModel2 = buildTestModel(); |
| mModel1.addAttendees(TEST_ADDRESSES2, mEmailValidator); |
| |
| // Updating a recurring event with a new attendee list |
| mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| // And a new start time to ensure the time fields aren't removed |
| mModel1.mOriginalStart = TEST_START; |
| |
| // The original model is assumed correct so drop the no good bit |
| mModel2.addAttendees(TEST_ADDRESSES4, null); |
| |
| mModel2.mRrule = null; |
| mModel2.mEnd = TEST_END; |
| mCurrentSaveTest = SAVE_EVENT_NORECUR_TO_RECUR; |
| |
| assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL)); |
| } |
| |
| private boolean verifySaveEventNonRecurringToRecurring(ArrayList<ContentProviderOperation> ops) |
| { |
| // Changing a non-recurring event to a recurring event should generate the same operations |
| // as just modifying a recurring event. |
| return verifySaveEventModifyRecurring(ops); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSaveEventUpdateNonRecurring() { |
| // Creates an original non-recurring and an updated recurring event model |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mModel1 = buildTestModel(); |
| mModel2 = buildTestModel(); |
| mModel1.addAttendees(TEST_ADDRESSES2, mEmailValidator); |
| |
| // Updating a recurring event with a new attendee list |
| mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| // And a new start time to ensure the time fields aren't removed |
| mModel1.mOriginalStart = TEST_START; |
| |
| // The original model is assumed correct so drop the no good bit |
| mModel2.addAttendees(TEST_ADDRESSES4, null); |
| |
| mModel2.mRrule = null; |
| mModel2.mEnd = TEST_END2; |
| mModel1.mRrule = null; |
| mModel1.mEnd = TEST_END2; |
| mCurrentSaveTest = SAVE_EVENT_MOD_NORECUR; |
| |
| assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL)); |
| } |
| |
| private boolean verifySaveEventUpdateNonRecurring(ArrayList<ContentProviderOperation> ops) { |
| ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); |
| int id = TEST_EVENT_ID; |
| mExpectedValues = buildNonRecurringTestValues(); |
| mExpectedValues.put(Events.HAS_ALARM, 0); |
| // This is tested elsewhere, used for convenience here |
| mHelper.checkTimeDependentFields(mModel1, mModel1, mExpectedValues, |
| EditEventHelper.MODIFY_ALL); |
| expectedOps.add(ContentProviderOperation.newUpdate(Uri.parse(mModel1.mUri)).withValues( |
| mExpectedValues).build()); |
| // This call has a separate unit test so we'll use it to simplify making the expected vals |
| mHelper.saveReminders(expectedOps, TEST_EVENT_ID, mModel1.mReminders, |
| mModel2.mReminders, false); |
| addOwnerAttendeeToOps(expectedOps); |
| addAttendeeChangesOps(expectedOps); |
| |
| assertEquals(expectedOps, ops); |
| return true; |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSaveEventModifySingleInstance() { |
| // Creates an original non-recurring and an updated recurring event model |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mModel1 = buildTestModel(); |
| mModel2 = buildTestModel(); |
| mModel1.addAttendees(TEST_ADDRESSES2, mEmailValidator); |
| |
| mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| // And a new start time to ensure the time fields aren't removed |
| mModel1.mOriginalStart = TEST_START; |
| |
| // The original model is assumed correct so drop the no good bit |
| mModel2.addAttendees(TEST_ADDRESSES4, null); |
| |
| // Modify the second instance of the event |
| long dayInMs = EditEventHelper.DAY_IN_SECONDS*1000; |
| mModel1.mRrule = null; |
| mModel1.mEnd = TEST_END + dayInMs; |
| mModel1.mStart += dayInMs; |
| mModel1.mOriginalStart = mModel1.mStart; |
| |
| mCurrentSaveTest = SAVE_EVENT_MOD_INSTANCE; |
| // Only modify this instance |
| assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_SELECTED)); |
| } |
| |
| private boolean verifySaveEventModifySingleInstance(ArrayList<ContentProviderOperation> ops) { |
| ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); |
| int id = 0; |
| mExpectedValues = buildNonRecurringTestValues(); |
| mExpectedValues.put(Events.HAS_ALARM, 0); |
| // This is tested elsewhere, used for convenience here |
| mHelper.checkTimeDependentFields(mModel1, mModel1, mExpectedValues, |
| EditEventHelper.MODIFY_ALL); |
| |
| moveExpectedTimeValuesForwardOneDay(); |
| mExpectedValues.put(Events.ORIGINAL_SYNC_ID, mModel2.mSyncId); |
| mExpectedValues.put(Events.ORIGINAL_INSTANCE_TIME, mModel1.mOriginalStart); |
| mExpectedValues.put(Events.ORIGINAL_ALL_DAY, 1); |
| |
| ContentProviderOperation.Builder b = ContentProviderOperation |
| .newInsert(Events.CONTENT_URI) |
| .withValues(mExpectedValues); |
| expectedOps.add(b.build()); |
| |
| mHelper.saveRemindersWithBackRef(expectedOps, id, mModel1.mReminders, |
| mModel2.mReminders, true); |
| |
| addOwnerAttendeeToOps(expectedOps, id); |
| |
| addTestAttendees(expectedOps, true, id); |
| |
| assertEquals(expectedOps, ops); |
| return true; |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSaveEventModifyAllFollowingWithNonRecurring() { |
| // Creates an original and an updated recurring event model. The update starts on the 2nd |
| // instance of the original. |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mModel1 = buildTestModel(); |
| mModel2 = buildTestModel(); |
| mModel1.addAttendees(TEST_ADDRESSES2, mEmailValidator); |
| |
| mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| mModel2.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| |
| // The original model is assumed correct so drop the no good bit |
| mModel2.addAttendees(TEST_ADDRESSES4, null); |
| |
| // Modify the second instance of the event |
| long dayInMs = EditEventHelper.DAY_IN_SECONDS*1000; |
| mModel1.mRrule = null; |
| mModel1.mEnd = TEST_END + dayInMs; |
| mModel1.mStart += dayInMs; |
| mModel1.mOriginalStart = mModel1.mStart; |
| |
| mCurrentSaveTest = SAVE_EVENT_ALLFOLLOW_TO_NORECUR; |
| // Only modify this instance |
| assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL_FOLLOWING)); |
| } |
| |
| private boolean verifySaveEventModifyAllFollowingWithNonRecurring( |
| ArrayList<ContentProviderOperation> ops) { |
| ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); |
| int id = 0; |
| mExpectedValues = buildNonRecurringTestValues(); |
| mExpectedValues.put(Events.HAS_ALARM, 0); |
| moveExpectedTimeValuesForwardOneDay(); |
| // This has a separate test |
| mHelper.updatePastEvents(expectedOps, mModel2, mModel1.mOriginalStart); |
| id = expectedOps.size(); |
| expectedOps.add(ContentProviderOperation |
| .newInsert(Events.CONTENT_URI) |
| .withValues(mExpectedValues) |
| .build()); |
| |
| mHelper.saveRemindersWithBackRef(expectedOps, id, mModel1.mReminders, |
| mModel2.mReminders, true); |
| |
| addOwnerAttendeeToOps(expectedOps, id); |
| |
| addTestAttendees(expectedOps, true, id); |
| |
| assertEquals(expectedOps, ops); |
| return true; |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSaveEventModifyAllFollowingFirstWithNonRecurring() { |
| // Creates an original recurring and an updated non-recurring event model for the first |
| // instance. This should replace the original event with a non-recurring event. |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mModel1 = buildTestModel(); |
| mModel2 = buildTestModel(); |
| mModel1.addAttendees(TEST_ADDRESSES2, mEmailValidator); |
| |
| mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| mModel2.mUri = mModel1.mUri; |
| // And a new start time to ensure the time fields aren't removed |
| mModel1.mOriginalStart = TEST_START; |
| |
| // The original model is assumed correct so drop the no good bit |
| mModel2.addAttendees(TEST_ADDRESSES3, null); |
| |
| // Move the event one day but keep original start set to the first instance |
| long dayInMs = EditEventHelper.DAY_IN_SECONDS*1000; |
| mModel1.mRrule = null; |
| mModel1.mEnd = TEST_END + dayInMs; |
| mModel1.mStart += dayInMs; |
| |
| mCurrentSaveTest = SAVE_EVENT_FIRST_TO_NORECUR; |
| // Only modify this instance |
| assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL_FOLLOWING)); |
| } |
| |
| private boolean verifySaveEventModifyAllFollowingFirstWithNonRecurring( |
| ArrayList<ContentProviderOperation> ops) { |
| |
| ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); |
| int id = 0; |
| mExpectedValues = buildNonRecurringTestValues(); |
| mExpectedValues.put(Events.HAS_ALARM, 0); |
| moveExpectedTimeValuesForwardOneDay(); |
| |
| expectedOps.add(ContentProviderOperation.newDelete(Uri.parse(mModel1.mUri)).build()); |
| id = expectedOps.size(); |
| expectedOps.add(ContentProviderOperation |
| .newInsert(Events.CONTENT_URI) |
| .withValues(mExpectedValues) |
| .build()); |
| |
| mHelper.saveRemindersWithBackRef(expectedOps, id, mModel1.mReminders, |
| mModel2.mReminders, true); |
| |
| addOwnerAttendeeToOps(expectedOps, id); |
| |
| addTestAttendees(expectedOps, true, id); |
| |
| assertEquals(expectedOps, ops); |
| return true; |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSaveEventModifyAllFollowingFirstWithRecurring() { |
| // Creates an original recurring and an updated recurring event model for the first instance |
| // This should replace the original event with a new recurrence |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mModel1 = buildTestModel(); |
| mModel2 = buildTestModel(); |
| mModel1.addAttendees(TEST_ADDRESSES2, mEmailValidator); |
| |
| mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| mModel2.mUri = mModel1.mUri; |
| // And a new start time to ensure the time fields aren't removed |
| mModel1.mOriginalStart = TEST_START; |
| |
| // The original model is assumed correct so drop the no good bit |
| mModel2.addAttendees(TEST_ADDRESSES4, null); |
| |
| // Move the event one day but keep original start set to the first instance |
| long dayInMs = EditEventHelper.DAY_IN_SECONDS*1000; |
| mModel1.mStart += dayInMs; |
| |
| mCurrentSaveTest = SAVE_EVENT_FIRST_TO_RECUR; |
| // Only modify this instance |
| assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL_FOLLOWING)); |
| } |
| |
| private boolean verifySaveEventModifyAllFollowingFirstWithRecurring( |
| ArrayList<ContentProviderOperation> ops) { |
| ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); |
| int br_id = 0; |
| mExpectedValues = buildTestValues(); |
| mExpectedValues.put(Events.HAS_ALARM, 0); |
| moveExpectedTimeValuesForwardOneDay(); |
| mExpectedValues.put(Events.DTEND, (Long)null); |
| // This is tested elsewhere, used for convenience here |
| mHelper.checkTimeDependentFields(mModel2, mModel1, mExpectedValues, |
| EditEventHelper.MODIFY_ALL_FOLLOWING); |
| |
| expectedOps.add(ContentProviderOperation.newUpdate(Uri.parse(mModel1.mUri)).withValues( |
| mExpectedValues).build()); |
| |
| // This call has a separate unit test so we'll use it to simplify making the expected vals |
| mHelper.saveReminders(expectedOps, TEST_EVENT_ID, mModel1.mReminders, |
| mModel2.mReminders, true); |
| |
| addOwnerAttendeeToOps(expectedOps); |
| addAttendeeChangesOps(expectedOps); |
| |
| assertEquals(expectedOps, ops); |
| return true; |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSaveEventModifyAllFollowingWithRecurring() { |
| // Creates an original recurring and an updated recurring event model |
| // for the second instance. This should end the original recurrence and add a new |
| // recurrence. |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mModel1 = buildTestModel(); |
| mModel2 = buildTestModel(); |
| mModel1.addAttendees(TEST_ADDRESSES2, mEmailValidator); |
| |
| mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| mModel2.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| |
| // The original model is assumed correct so drop the no good bit |
| mModel2.addAttendees(TEST_ADDRESSES4, null); |
| |
| // Move the event one day and the original start so it references the second instance |
| long dayInMs = EditEventHelper.DAY_IN_SECONDS*1000; |
| mModel1.mStart += dayInMs; |
| mModel1.mOriginalStart = mModel1.mStart; |
| |
| mCurrentSaveTest = SAVE_EVENT_ALLFOLLOW_TO_RECUR; |
| // Only modify this instance |
| assertTrue(mHelper.saveEvent(mModel1, mModel2, EditEventHelper.MODIFY_ALL_FOLLOWING)); |
| } |
| |
| private boolean verifySaveEventModifyAllFollowingWithRecurring( |
| ArrayList<ContentProviderOperation> ops) { |
| ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); |
| int br_id = 0; |
| mExpectedValues = buildTestValues(); |
| mExpectedValues.put(Events.HAS_ALARM, 0); |
| moveExpectedTimeValuesForwardOneDay(); |
| mExpectedValues.put(Events.DTEND, (Long)null); |
| // This is tested elsewhere, used for convenience here |
| mHelper.updatePastEvents(expectedOps, mModel2, mModel1.mOriginalStart); |
| |
| br_id = expectedOps.size(); |
| expectedOps.add(ContentProviderOperation |
| .newInsert(Events.CONTENT_URI) |
| .withValues(mExpectedValues) |
| .build()); |
| |
| // This call has a separate unit test so we'll use it to simplify making the expected vals |
| mHelper.saveRemindersWithBackRef(expectedOps, br_id, mModel1.mReminders, |
| mModel2.mReminders, true); |
| |
| addOwnerAttendeeToOps(expectedOps, br_id); |
| |
| addTestAttendees(expectedOps, true, br_id); |
| |
| assertEquals(expectedOps, ops); |
| return true; |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testGetAddressesFromList() { |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| LinkedHashSet<Rfc822Token> expected = new LinkedHashSet<Rfc822Token>(); |
| expected.add(new Rfc822Token(null, "ad1@email.com", "")); |
| expected.add(new Rfc822Token("First Last", "first@email.com", "comment")); |
| expected.add(new Rfc822Token(null, "one.two.three@email.grue", "")); |
| |
| LinkedHashSet<Rfc822Token> actual = mHelper.getAddressesFromList(TEST_ADDRESSES, |
| new Rfc822Validator(null)); |
| assertEquals(expected, actual); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testConstructDefaultStartTime() { |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| long now = 0; |
| long expected = now + 30 * DateUtils.MINUTE_IN_MILLIS; |
| assertEquals(expected, mHelper.constructDefaultStartTime(now)); |
| |
| // 2:00 -> 2:30 |
| now = 1262340000000L; // Fri Jan 01 2010 02:00:00 GMT-0800 (PST) |
| expected = now + 30 * DateUtils.MINUTE_IN_MILLIS; |
| assertEquals(expected, mHelper.constructDefaultStartTime(now)); |
| |
| // 2:01 -> 2:30 |
| now += DateUtils.MINUTE_IN_MILLIS; |
| assertEquals(expected, mHelper.constructDefaultStartTime(now)); |
| |
| // 2:02 -> 2:30 |
| now += DateUtils.MINUTE_IN_MILLIS; |
| assertEquals(expected, mHelper.constructDefaultStartTime(now)); |
| |
| // 2:32 -> 3:00 |
| now += 30 * DateUtils.MINUTE_IN_MILLIS; |
| expected += 30 * DateUtils.MINUTE_IN_MILLIS; |
| assertEquals(expected, mHelper.constructDefaultStartTime(now)); |
| |
| // 2:33 -> 3:00 |
| now += DateUtils.MINUTE_IN_MILLIS; |
| assertEquals(expected, mHelper.constructDefaultStartTime(now)); |
| |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testConstructDefaultEndTime() { |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| long start = 1262340000000L; |
| long expected = start + DateUtils.HOUR_IN_MILLIS; |
| assertEquals(expected, mHelper.constructDefaultEndTime(start)); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testCheckTimeDependentFieldsNoChanges() { |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mModel1 = buildTestModel(); |
| mModel2 = buildTestModel(); |
| mModel2.mRrule = null; |
| |
| mValues = buildTestValues(); |
| mExpectedValues = buildTestValues(); |
| |
| // if any time/recurrence vals are different but there's no new rrule it |
| // shouldn't change |
| mHelper.checkTimeDependentFields(mModel1, mModel2, mValues, EditEventHelper.MODIFY_ALL); |
| assertEquals(mExpectedValues, mValues); |
| |
| // also, if vals are different and it's not modifying all it shouldn't |
| // change. |
| mModel2.mRrule = "something else"; |
| mHelper.checkTimeDependentFields(mModel1, mModel2, mValues, |
| EditEventHelper.MODIFY_SELECTED); |
| assertEquals(mExpectedValues, mValues); |
| |
| // if vals changed and modify all is selected dtstart should be updated |
| // by the difference |
| // between originalStart and start |
| mModel2.mOriginalStart = mModel2.mStart + 60000; // set the old time to |
| // one minute later |
| mModel2.mStart += 120000; // move the event another 1 minute. |
| |
| // shouldn't change for an allday event |
| // expectedVals.put(Events.DTSTART, mModel1.mStart + 60000); // should |
| // now be 1 minute later |
| // dtstart2 shouldn't change since it gets rezeroed in the local |
| // timezone for allDay events |
| |
| mHelper.checkTimeDependentFields(mModel1, mModel2, mValues, |
| EditEventHelper.MODIFY_SELECTED); |
| assertEquals(mExpectedValues, mValues); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testCheckTimeDependentFieldsChanges() { |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mModel1 = buildTestModel(); |
| mModel2 = buildTestModel(); |
| mModel2.mRrule = null; |
| |
| mValues = buildTestValues(); |
| mExpectedValues = buildTestValues(); |
| |
| // if all the time values are the same it should remove them from vals |
| mModel2.mRrule = mModel1.mRrule; |
| mModel2.mStart = mModel1.mStart; |
| mModel2.mOriginalStart = mModel2.mStart; |
| |
| mExpectedValues.remove(Events.DTSTART); |
| mExpectedValues.remove(Events.DTEND); |
| mExpectedValues.remove(Events.DURATION); |
| mExpectedValues.remove(Events.ALL_DAY); |
| mExpectedValues.remove(Events.RRULE); |
| mExpectedValues.remove(Events.EVENT_TIMEZONE); |
| |
| mHelper.checkTimeDependentFields(mModel1, mModel2, mValues, |
| EditEventHelper.MODIFY_SELECTED); |
| assertEquals(mExpectedValues, mValues); |
| |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testUpdatePastEvents() { |
| ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); |
| ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); |
| long initialBeginTime = 1472864400000L; // Sep 3, 2016, 12AM UTC time |
| mValues = new ContentValues(); |
| |
| mModel1 = buildTestModel(); |
| mModel1.mUri = (AUTHORITY_URI + TEST_EVENT_ID); |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| |
| mValues.put(Events.RRULE, "FREQ=DAILY;UNTIL=20160903;WKST=SU"); // yyyymmddThhmmssZ |
| mValues.put(Events.DTSTART, TEST_START); |
| |
| ContentProviderOperation.Builder b = ContentProviderOperation.newUpdate( |
| Uri.parse(mModel1.mUri)).withValues(mValues); |
| expectedOps.add(b.build()); |
| |
| mHelper.updatePastEvents(ops, mModel1, initialBeginTime); |
| assertEquals(expectedOps, ops); |
| |
| mModel1.mAllDay = false; |
| |
| mValues.put(Events.RRULE, "FREQ=DAILY;UNTIL=20160903T005959Z;WKST=SU"); // yyyymmddThhmmssZ |
| |
| expectedOps.clear(); |
| b = ContentProviderOperation.newUpdate(Uri.parse(mModel1.mUri)).withValues(mValues); |
| expectedOps.add(b.build()); |
| |
| ops.clear(); |
| mHelper.updatePastEvents(ops, mModel1, initialBeginTime); |
| assertEquals(expectedOps, ops); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testConstructReminderLabel() { |
| mActivity = buildTestContext(); |
| |
| String label = EventViewUtils.constructReminderLabel(mActivity, 35, true); |
| assertEquals("35 mins", label); |
| |
| label = EventViewUtils.constructReminderLabel(mActivity, 72, false); |
| assertEquals("72 minutes", label); |
| |
| label = EventViewUtils.constructReminderLabel(mActivity, 60, true); |
| assertEquals("1 hours", label); |
| |
| label = EventViewUtils.constructReminderLabel(mActivity, 60 * 48, true); |
| assertEquals("2 days", label); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testIsSameEvent() { |
| mModel1 = new CalendarEventModel(); |
| mModel2 = new CalendarEventModel(); |
| |
| mModel1.mId = 1; |
| mModel1.mCalendarId = 1; |
| mModel2.mId = 1; |
| mModel2.mCalendarId = 1; |
| |
| // considered the same if the event and calendar ids both match |
| assertTrue(EditEventHelper.isSameEvent(mModel1, mModel2)); |
| |
| mModel2.mId = 2; |
| assertFalse(EditEventHelper.isSameEvent(mModel1, mModel2)); |
| |
| mModel2.mId = 1; |
| mModel2.mCalendarId = 2; |
| assertFalse(EditEventHelper.isSameEvent(mModel1, mModel2)); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSaveReminders() { |
| ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); |
| ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); |
| long eventId = TEST_EVENT_ID; |
| ArrayList<ReminderEntry> reminders = new ArrayList<ReminderEntry>(); |
| ArrayList<ReminderEntry> originalReminders = new ArrayList<ReminderEntry>(); |
| boolean forceSave = true; |
| boolean result; |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| assertNotNull(mHelper); |
| |
| // First test forcing a delete with no reminders. |
| String where = Reminders.EVENT_ID + "=?"; |
| String[] args = new String[] {Long.toString(eventId)}; |
| ContentProviderOperation.Builder b = |
| ContentProviderOperation.newDelete(Reminders.CONTENT_URI); |
| b.withSelection(where, args); |
| expectedOps.add(b.build()); |
| |
| result = mHelper.saveReminders(ops, eventId, reminders, originalReminders, forceSave); |
| assertTrue(result); |
| assertEquals(expectedOps, ops); |
| |
| // Now test calling save with identical reminders and no forcing |
| reminders.add(ReminderEntry.valueOf(5)); |
| reminders.add(ReminderEntry.valueOf(10)); |
| reminders.add(ReminderEntry.valueOf(15)); |
| |
| originalReminders.add(ReminderEntry.valueOf(5)); |
| originalReminders.add(ReminderEntry.valueOf(10)); |
| originalReminders.add(ReminderEntry.valueOf(15)); |
| |
| forceSave = false; |
| |
| ops.clear(); |
| |
| // Should fail to create any ops since nothing changed |
| result = mHelper.saveReminders(ops, eventId, reminders, originalReminders, forceSave); |
| assertFalse(result); |
| assertEquals(0, ops.size()); |
| |
| //Now test adding a single reminder |
| originalReminders.remove(2); |
| |
| addExpectedMinutes(expectedOps); |
| |
| result = mHelper.saveReminders(ops, eventId, reminders, originalReminders, forceSave); |
| assertTrue(result); |
| assertEquals(expectedOps, ops); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSaveRemindersWithBackRef() { |
| ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); |
| ArrayList<ContentProviderOperation> expectedOps = new ArrayList<ContentProviderOperation>(); |
| long eventId = TEST_EVENT_ID; |
| ArrayList<ReminderEntry> reminders = new ArrayList<ReminderEntry>(); |
| ArrayList<ReminderEntry> originalReminders = new ArrayList<ReminderEntry>(); |
| boolean forceSave = true; |
| boolean result; |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| assertNotNull(mHelper); |
| |
| // First test forcing a delete with no reminders. |
| ContentProviderOperation.Builder b = |
| ContentProviderOperation.newDelete(Reminders.CONTENT_URI); |
| b.withSelection(Reminders.EVENT_ID + "=?", new String[1]); |
| b.withSelectionBackReference(0, TEST_EVENT_INDEX_ID); |
| expectedOps.add(b.build()); |
| |
| result = |
| mHelper.saveRemindersWithBackRef(ops, TEST_EVENT_INDEX_ID, reminders, |
| originalReminders, forceSave); |
| assertTrue(result); |
| assertEquals(expectedOps, ops); |
| |
| // Now test calling save with identical reminders and no forcing |
| reminders.add(ReminderEntry.valueOf(5)); |
| reminders.add(ReminderEntry.valueOf(10)); |
| reminders.add(ReminderEntry.valueOf(15)); |
| |
| originalReminders.add(ReminderEntry.valueOf(5)); |
| originalReminders.add(ReminderEntry.valueOf(10)); |
| originalReminders.add(ReminderEntry.valueOf(15)); |
| |
| forceSave = false; |
| |
| ops.clear(); |
| |
| result = mHelper.saveRemindersWithBackRef(ops, ops.size(), reminders, originalReminders, |
| forceSave); |
| assertFalse(result); |
| assertEquals(0, ops.size()); |
| |
| //Now test adding a single reminder |
| originalReminders.remove(2); |
| |
| addExpectedMinutesWithBackRef(expectedOps); |
| |
| result = mHelper.saveRemindersWithBackRef(ops, ops.size(), reminders, originalReminders, |
| forceSave); |
| assertTrue(result); |
| assertEquals(expectedOps, ops); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testIsFirstEventInSeries() { |
| mModel1 = new CalendarEventModel(); |
| mModel2 = new CalendarEventModel(); |
| |
| // It's considered the first event if the original start of the new model matches the |
| // start of the old model |
| mModel1.mOriginalStart = 100; |
| mModel1.mStart = 200; |
| mModel2.mOriginalStart = 100; |
| mModel2.mStart = 100; |
| |
| assertTrue(EditEventHelper.isFirstEventInSeries(mModel1, mModel2)); |
| |
| mModel1.mOriginalStart = 80; |
| assertFalse(EditEventHelper.isFirstEventInSeries(mModel1, mModel2)); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testAddRecurrenceRule() { |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| mValues = new ContentValues(); |
| mExpectedValues = new ContentValues(); |
| mModel1 = new CalendarEventModel(); |
| |
| mExpectedValues.put(Events.RRULE, "Weekly, Monday"); |
| mExpectedValues.put(Events.DURATION, "P60S"); |
| mExpectedValues.put(Events.DTEND, (Long) null); |
| |
| mModel1.mRrule = "Weekly, Monday"; |
| mModel1.mStart = 1; |
| mModel1.mEnd = 60001; |
| mModel1.mAllDay = false; |
| |
| mHelper.addRecurrenceRule(mValues, mModel1); |
| assertEquals(mExpectedValues, mValues); |
| |
| mExpectedValues.put(Events.DURATION, "P1D"); |
| |
| mModel1.mAllDay = true; |
| mValues.clear(); |
| |
| mHelper.addRecurrenceRule(mValues, mModel1); |
| assertEquals(mExpectedValues, mValues); |
| |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testUpdateRecurrenceRule() { |
| int selection = EditEventHelper.DOES_NOT_REPEAT; |
| int weekStart = Calendar.SUNDAY; |
| mModel1 = new CalendarEventModel(); |
| mModel1.mTimezone = Time.TIMEZONE_UTC; |
| mModel1.mStart = 1272665741000L; // Fri, April 30th ~ 3:17PM |
| |
| mModel1.mRrule = "This should go away"; |
| |
| EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); |
| assertNull(mModel1.mRrule); |
| |
| mModel1.mRrule = "This shouldn't change"; |
| selection = EditEventHelper.REPEATS_CUSTOM; |
| |
| EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); |
| assertEquals("This shouldn't change", mModel1.mRrule); |
| |
| selection = EditEventHelper.REPEATS_DAILY; |
| |
| EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); |
| assertEquals("FREQ=DAILY;WKST=SU", mModel1.mRrule); |
| |
| selection = EditEventHelper.REPEATS_EVERY_WEEKDAY; |
| |
| EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); |
| assertEquals("FREQ=WEEKLY;WKST=SU;BYDAY=MO,TU,WE,TH,FR", mModel1.mRrule); |
| |
| selection = EditEventHelper.REPEATS_WEEKLY_ON_DAY; |
| |
| EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); |
| assertEquals("FREQ=WEEKLY;WKST=SU;BYDAY=FR", mModel1.mRrule); |
| |
| selection = EditEventHelper.REPEATS_MONTHLY_ON_DAY; |
| |
| EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); |
| assertEquals("FREQ=MONTHLY;WKST=SU;BYMONTHDAY=30", mModel1.mRrule); |
| |
| selection = EditEventHelper.REPEATS_MONTHLY_ON_DAY_COUNT; |
| |
| EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); |
| assertEquals("FREQ=MONTHLY;WKST=SU;BYDAY=-1FR", mModel1.mRrule); |
| |
| selection = EditEventHelper.REPEATS_YEARLY; |
| |
| EditEventHelper.updateRecurrenceRule(selection, mModel1, weekStart); |
| assertEquals("FREQ=YEARLY;WKST=SU", mModel1.mRrule); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testSetModelFromCursor() { |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| MatrixCursor c = new MatrixCursor(EditEventHelper.EVENT_PROJECTION); |
| c.addRow(TEST_CURSOR_DATA); |
| |
| mModel1 = new CalendarEventModel(); |
| mModel2 = buildTestModel(); |
| |
| EditEventHelper.setModelFromCursor(mModel1, c); |
| assertEquals(mModel1, mModel2); |
| |
| TEST_CURSOR_DATA[EditEventHelper.EVENT_INDEX_ALL_DAY] = "0"; |
| c.close(); |
| c = new MatrixCursor(EditEventHelper.EVENT_PROJECTION); |
| c.addRow(TEST_CURSOR_DATA); |
| |
| mModel2.mAllDay = false; |
| mModel2.mStart = TEST_START; // UTC time |
| |
| EditEventHelper.setModelFromCursor(mModel1, c); |
| assertEquals(mModel1, mModel2); |
| |
| TEST_CURSOR_DATA[EditEventHelper.EVENT_INDEX_RRULE] = null; |
| c.close(); |
| c = new MatrixCursor(EditEventHelper.EVENT_PROJECTION); |
| c.addRow(TEST_CURSOR_DATA); |
| |
| mModel2.mRrule = null; |
| mModel2.mEnd = TEST_END; |
| mModel2.mDuration = null; |
| |
| EditEventHelper.setModelFromCursor(mModel1, c); |
| assertEquals(mModel1, mModel2); |
| |
| TEST_CURSOR_DATA[EditEventHelper.EVENT_INDEX_ALL_DAY] = "1"; |
| c.close(); |
| c = new MatrixCursor(EditEventHelper.EVENT_PROJECTION); |
| c.addRow(TEST_CURSOR_DATA); |
| |
| mModel2.mAllDay = true; |
| mModel2.mStart = TEST_START; // Monday, May 3rd, midnight |
| mModel2.mEnd = TEST_END; // Tuesday, May 4th, midnight |
| |
| EditEventHelper.setModelFromCursor(mModel1, c); |
| assertEquals(mModel1, mModel2); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testGetContentValuesFromModel() { |
| mActivity = buildTestContext(); |
| mHelper = new EditEventHelper(mActivity, null); |
| mExpectedValues = buildTestValues(); |
| mModel1 = buildTestModel(); |
| |
| ContentValues values = mHelper.getContentValuesFromModel(mModel1); |
| assertEquals(mExpectedValues, values); |
| |
| mModel1.mRrule = null; |
| mModel1.mEnd = TEST_END; |
| |
| mExpectedValues.put(Events.RRULE, (String) null); |
| mExpectedValues.put(Events.DURATION, (String) null); |
| mExpectedValues.put(Events.DTEND, TEST_END); // UTC time |
| |
| values = mHelper.getContentValuesFromModel(mModel1); |
| assertEquals(mExpectedValues, values); |
| |
| mModel1.mAllDay = false; |
| |
| mExpectedValues.put(Events.ALL_DAY, 0); |
| mExpectedValues.put(Events.DTSTART, TEST_START); |
| mExpectedValues.put(Events.DTEND, TEST_END); |
| // not an allday event so timezone isn't modified |
| mExpectedValues.put(Events.EVENT_TIMEZONE, "UTC"); |
| |
| values = mHelper.getContentValuesFromModel(mModel1); |
| assertEquals(mExpectedValues, values); |
| } |
| |
| @Smoke |
| @SmallTest |
| public void testExtractDomain() { |
| String domain = EditEventHelper.extractDomain("test.email@gmail.com"); |
| assertEquals("gmail.com", domain); |
| |
| domain = EditEventHelper.extractDomain("bademail.no#$%at symbol"); |
| assertNull(domain); |
| } |
| |
| private void addExpectedMinutes(ArrayList<ContentProviderOperation> expectedOps) { |
| ContentProviderOperation.Builder b; |
| mValues = new ContentValues(); |
| |
| mValues.clear(); |
| mValues.put(Reminders.MINUTES, 5); |
| mValues.put(Reminders.METHOD, Reminders.METHOD_DEFAULT); |
| mValues.put(Reminders.EVENT_ID, TEST_EVENT_ID); |
| b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(mValues); |
| expectedOps.add(b.build()); |
| |
| mValues.clear(); |
| mValues.put(Reminders.MINUTES, 10); |
| mValues.put(Reminders.METHOD, Reminders.METHOD_DEFAULT); |
| mValues.put(Reminders.EVENT_ID, TEST_EVENT_ID); |
| b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(mValues); |
| expectedOps.add(b.build()); |
| |
| mValues.clear(); |
| mValues.put(Reminders.MINUTES, 15); |
| mValues.put(Reminders.METHOD, Reminders.METHOD_DEFAULT); |
| mValues.put(Reminders.EVENT_ID, TEST_EVENT_ID); |
| b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(mValues); |
| expectedOps.add(b.build()); |
| } |
| |
| private void addExpectedMinutesWithBackRef(ArrayList<ContentProviderOperation> expectedOps) { |
| ContentProviderOperation.Builder b; |
| mValues = new ContentValues(); |
| |
| mValues.clear(); |
| mValues.put(Reminders.MINUTES, 5); |
| mValues.put(Reminders.METHOD, Reminders.METHOD_DEFAULT); |
| b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(mValues); |
| b.withValueBackReference(Reminders.EVENT_ID, TEST_EVENT_INDEX_ID); |
| expectedOps.add(b.build()); |
| |
| mValues.clear(); |
| mValues.put(Reminders.MINUTES, 10); |
| mValues.put(Reminders.METHOD, Reminders.METHOD_DEFAULT); |
| b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(mValues); |
| b.withValueBackReference(Reminders.EVENT_ID, TEST_EVENT_INDEX_ID); |
| expectedOps.add(b.build()); |
| |
| mValues.clear(); |
| mValues.put(Reminders.MINUTES, 15); |
| mValues.put(Reminders.METHOD, Reminders.METHOD_DEFAULT); |
| b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(mValues); |
| b.withValueBackReference(Reminders.EVENT_ID, TEST_EVENT_INDEX_ID); |
| expectedOps.add(b.build()); |
| } |
| |
| private static void assertEquals(ArrayList<ContentProviderOperation> expected, |
| ArrayList<ContentProviderOperation> actual) { |
| if (expected == null) { |
| assertNull(actual); |
| } |
| int size = expected.size(); |
| |
| assertEquals(size, actual.size()); |
| |
| for (int i = 0; i < size; i++) { |
| assertTrue("At index " + i + ", expected:\n" + String.valueOf(expected.get(i)) + |
| "\nActual:\n" + String.valueOf(actual.get(i)), |
| cpoEquals(expected.get(i), actual.get(i))); |
| } |
| |
| } |
| |
| private static boolean cpoEquals(ContentProviderOperation cpo1, ContentProviderOperation cpo2) { |
| if (cpo1 == null && cpo2 != null) { |
| return false; |
| } |
| if (cpo1 == cpo2) { |
| return true; |
| } |
| if (cpo2 == null) { |
| return false; |
| } |
| |
| // It turns out we can't trust the toString() of the ContentProviderOperations to be |
| // consistent, so we have to do the comparison manually. |
| // |
| // Start by splitting by commas, so that we can compare each key-value pair individually. |
| String[] operations1 = cpo1.toString().split(","); |
| String[] operations2 = cpo2.toString().split(","); |
| // The two numbers of operations must be equal. |
| if (operations1.length != operations2.length) { |
| return false; |
| } |
| // Iterate through the key-value pairs and separate out the key and value. |
| // The value may be either a single string, or a series of further key-value pairs |
| // that are separated by " ", with a "=" between the key and value. |
| for (int i = 0; i < operations1.length; i++) { |
| String operation1 = operations1[i]; |
| String operation2 = operations2[i]; |
| // Limit the array to length 2 in case a ":" appears in the value. |
| String[] keyValue1 = operation1.split(":", 2); |
| String[] keyValue2 = operation2.split(":", 2); |
| // If the key doesn't match, return false. |
| if (!keyValue1[0].equals(keyValue2[0])) { |
| return false; |
| } |
| // First just check if the value matches up. If so, we're good to go. |
| if (keyValue1[1].equals(keyValue2[1])) { |
| continue; |
| } |
| // If not, we need to try splitting the value by " " and sorting those keyvalue pairs. |
| // Note that these are trimmed first to ensure we're not thrown off by extra whitespace. |
| String[] valueKeyValuePairs1 = keyValue1[1].trim().split(" "); |
| String[] valueKeyValuePairs2 = keyValue2[1].trim().split(" "); |
| // Sort the value's keyvalue pairs alphabetically, and now compare them to each other. |
| Arrays.sort(valueKeyValuePairs1); |
| Arrays.sort(valueKeyValuePairs2); |
| for (int j = 0; j < valueKeyValuePairs1.length; j++) { |
| if (!valueKeyValuePairs1[j].equals(valueKeyValuePairs2[j])) { |
| return false; |
| } |
| } |
| } |
| |
| // If we make it all the way through without finding anything different, return true. |
| return true; |
| } |
| |
| // Generates a default model for testing. Should match up with |
| // generateTestValues |
| private CalendarEventModel buildTestModel() { |
| CalendarEventModel model = new CalendarEventModel(); |
| model.mId = TEST_EVENT_ID; |
| model.mTitle = "The_Question"; |
| model.mDescription = "Evaluating_Life_the_Universe_and_Everything"; |
| model.mLocation = "Earth_Mk2"; |
| model.mAllDay = true; |
| model.mHasAlarm = false; |
| model.mCalendarId = 2; |
| model.mStart = TEST_START; // Monday, May 3rd, local Time |
| model.mDuration = "P3652421990D"; |
| // The model uses the local timezone for allday |
| model.mTimezone = "UTC"; |
| model.mRrule = "FREQ=DAILY;WKST=SU"; |
| model.mSyncId = "unique_per_calendar_stuff"; |
| model.mAvailability = 0; |
| model.mAccessLevel = 2; // This is one less than the values written if >0 |
| model.mOwnerAccount = "steve@gmail.com"; |
| model.mHasAttendeeData = true; |
| model.mOrganizer = "organizer@gmail.com"; |
| model.mIsOrganizer = false; |
| model.mGuestsCanModify = false; |
| model.mEventStatus = Events.STATUS_CONFIRMED; |
| int displayColor = Utils.getDisplayColorFromColor(-2350809); |
| model.setEventColor(displayColor); |
| model.mCalendarAccountName = "steve.owner@gmail.com"; |
| model.mCalendarAccountType = "gmail.com"; |
| EventColorCache cache = new EventColorCache(); |
| cache.insertColor("steve.owner@gmail.com", "gmail.com", displayColor, 12); |
| model.mEventColorCache = cache; |
| model.mModelUpdatedWithEventCursor = true; |
| return model; |
| } |
| |
| // Generates a default set of values for testing. Should match up with |
| // generateTestModel |
| private ContentValues buildTestValues() { |
| ContentValues values = new ContentValues(); |
| |
| values.put(Events.CALENDAR_ID, 2L); |
| values.put(Events.EVENT_TIMEZONE, "UTC"); // Allday events are converted |
| // to UTC for the db |
| values.put(Events.TITLE, "The_Question"); |
| values.put(Events.ALL_DAY, 1); |
| values.put(Events.DTSTART, TEST_START); // Monday, May 3rd, midnight UTC time |
| values.put(Events.HAS_ATTENDEE_DATA, 1); |
| |
| values.put(Events.RRULE, "FREQ=DAILY;WKST=SU"); |
| values.put(Events.DURATION, "P3652421990D"); |
| values.put(Events.DTEND, (Long) null); |
| values.put(Events.DESCRIPTION, "Evaluating_Life_the_Universe_and_Everything"); |
| values.put(Events.EVENT_LOCATION, "Earth_Mk2"); |
| values.put(Events.AVAILABILITY, 0); |
| values.put(Events.STATUS, Events.STATUS_CONFIRMED); |
| values.put(Events.ACCESS_LEVEL, 3); // This is one more than the model if |
| // >0 |
| values.put(Events.EVENT_COLOR_KEY, 12); |
| |
| return values; |
| } |
| |
| private ContentValues buildNonRecurringTestValues() { |
| ContentValues values = buildTestValues(); |
| values.put(Events.DURATION, (String)null); |
| values.put(Events.DTEND, TEST_END); |
| values.put(Events.RRULE, (String)null); |
| return values; |
| } |
| |
| // This gets called by EditEventHelper to read or write the data |
| class TestProvider extends ContentProvider { |
| int index = 0; |
| |
| public TestProvider() { |
| } |
| |
| @Override |
| public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, |
| String orderBy) { |
| return null; |
| } |
| |
| @Override |
| public int delete(Uri uri, String selection, String[] selectionArgs) { |
| return 0; |
| } |
| |
| @Override |
| public String getType(Uri uri) { |
| return null; |
| } |
| |
| @Override |
| public boolean onCreate() { |
| return false; |
| } |
| |
| @Override |
| public Uri insert(Uri uri, ContentValues values) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { |
| // TODO Auto-generated method stub |
| return 0; |
| } |
| } |
| |
| } |