Home | History | Annotate | Download | only in adapter
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of 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,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.exchange.adapter;
     18 
     19 import com.android.exchange.adapter.AbstractSyncAdapter.Operation;
     20 import com.android.exchange.adapter.CalendarSyncAdapter.CalendarOperations;
     21 import com.android.exchange.adapter.CalendarSyncAdapter.EasCalendarSyncParser;
     22 import com.android.exchange.provider.MockProvider;
     23 
     24 import android.content.ContentProviderOperation;
     25 import android.content.ContentValues;
     26 import android.content.Context;
     27 import android.content.OperationApplicationException;
     28 import android.content.res.Resources;
     29 import android.database.Cursor;
     30 import android.os.RemoteException;
     31 import android.provider.CalendarContract.Attendees;
     32 import android.provider.CalendarContract.Events;
     33 import android.test.IsolatedContext;
     34 import android.test.RenamingDelegatingContext;
     35 import android.test.mock.MockContentResolver;
     36 import android.test.mock.MockContext;
     37 import android.test.suitebuilder.annotation.MediumTest;
     38 
     39 import java.io.ByteArrayInputStream;
     40 import java.io.File;
     41 import java.io.IOException;
     42 import java.util.ArrayList;
     43 import java.util.GregorianCalendar;
     44 import java.util.List;
     45 import java.util.TimeZone;
     46 
     47 /**
     48  * You can run this entire test case with:
     49  *   runtest -c com.android.exchange.adapter.CalendarSyncAdapterTests exchange
     50  */
     51 @MediumTest
     52 public class CalendarSyncAdapterTests extends SyncAdapterTestCase<CalendarSyncAdapter> {
     53     private static final String[] ATTENDEE_PROJECTION = new String[] {Attendees.ATTENDEE_EMAIL,
     54             Attendees.ATTENDEE_NAME, Attendees.ATTENDEE_STATUS};
     55     private static final int ATTENDEE_EMAIL = 0;
     56     private static final int ATTENDEE_NAME = 1;
     57     private static final int ATTENDEE_STATUS = 2;
     58 
     59     private static final String SINGLE_ATTENDEE_EMAIL = "attendee (at) host.com";
     60     private static final String SINGLE_ATTENDEE_NAME = "Bill Attendee";
     61 
     62     private Context mMockContext;
     63     private MockContentResolver mMockResolver;
     64 
     65     // This is the US/Pacific time zone as a base64-encoded TIME_ZONE_INFORMATION structure, as
     66     // it would appear coming from an Exchange server
     67     private static final String TEST_TIME_ZONE = "4AEAAFAAYQBjAGkAZgBpAGMAIABTAHQAYQBuAGQAYQByA" +
     68         "GQAIABUAGkAbQBlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAABAAIAAAAAAAAAAAAAAFAAY" +
     69         "QBjAGkAZgBpAGMAIABEAGEAeQBsAGkAZwBoAHQAIABUAGkAbQBlAAAAAAAAAAAAAAAAAAAAAAAAA" +
     70         "AAAAAAAAAMAAAACAAIAAAAAAAAAxP///w==";
     71 
     72     private class MockContext2 extends MockContext {
     73 
     74         @Override
     75         public Resources getResources() {
     76             return getContext().getResources();
     77         }
     78 
     79         @Override
     80         public File getDir(String name, int mode) {
     81             // name the directory so the directory will be separated from
     82             // one created through the regular Context
     83             return getContext().getDir("mockcontext2_" + name, mode);
     84         }
     85 
     86         @Override
     87         public Context getApplicationContext() {
     88             return this;
     89         }
     90     }
     91 
     92     @Override
     93     public void setUp() throws Exception {
     94         super.setUp();
     95 
     96         mMockResolver = new MockContentResolver();
     97         final String filenamePrefix = "test.";
     98         RenamingDelegatingContext targetContextWrapper = new
     99         RenamingDelegatingContext(
    100                 new MockContext2(), // The context that most methods are delegated to
    101                 getContext(), // The context that file methods are delegated to
    102                 filenamePrefix);
    103         mMockContext = new IsolatedContext(mMockResolver, targetContextWrapper);
    104         mMockResolver.addProvider(MockProvider.AUTHORITY, new MockProvider(mMockContext));
    105     }
    106 
    107     public CalendarSyncAdapterTests() {
    108         super();
    109     }
    110 
    111     public void testSetTimeRelatedValues_NonRecurring() throws IOException {
    112         CalendarSyncAdapter adapter = getTestSyncAdapter(CalendarSyncAdapter.class);
    113         EasCalendarSyncParser p = adapter.new EasCalendarSyncParser(getTestInputStream(), adapter);
    114         ContentValues cv = new ContentValues();
    115         // Basic, one-time meeting lasting an hour
    116         GregorianCalendar startCalendar = new GregorianCalendar(2010, 5, 10, 8, 30);
    117         Long startTime = startCalendar.getTimeInMillis();
    118         GregorianCalendar endCalendar = new GregorianCalendar(2010, 5, 10, 9, 30);
    119         Long endTime = endCalendar.getTimeInMillis();
    120 
    121         p.setTimeRelatedValues(cv, startTime, endTime, 0);
    122         assertNull(cv.getAsInteger(Events.DURATION));
    123         assertEquals(startTime, cv.getAsLong(Events.DTSTART));
    124         assertEquals(endTime, cv.getAsLong(Events.DTEND));
    125         assertEquals(endTime, cv.getAsLong(Events.LAST_DATE));
    126         assertNull(cv.getAsString(Events.EVENT_TIMEZONE));
    127     }
    128 
    129     public void testSetTimeRelatedValues_Recurring() throws IOException {
    130         CalendarSyncAdapter adapter = getTestSyncAdapter(CalendarSyncAdapter.class);
    131         EasCalendarSyncParser p = adapter.new EasCalendarSyncParser(getTestInputStream(), adapter);
    132         ContentValues cv = new ContentValues();
    133         // Recurring meeting lasting an hour
    134         GregorianCalendar startCalendar = new GregorianCalendar(2010, 5, 10, 8, 30);
    135         Long startTime = startCalendar.getTimeInMillis();
    136         GregorianCalendar endCalendar = new GregorianCalendar(2010, 5, 10, 9, 30);
    137         Long endTime = endCalendar.getTimeInMillis();
    138         cv.put(Events.RRULE, "FREQ=DAILY");
    139         p.setTimeRelatedValues(cv, startTime, endTime, 0);
    140         assertEquals("P60M", cv.getAsString(Events.DURATION));
    141         assertEquals(startTime, cv.getAsLong(Events.DTSTART));
    142         assertNull(cv.getAsLong(Events.DTEND));
    143         assertNull(cv.getAsLong(Events.LAST_DATE));
    144         assertNull(cv.getAsString(Events.EVENT_TIMEZONE));
    145     }
    146 
    147     public void testSetTimeRelatedValues_AllDay() throws IOException {
    148         CalendarSyncAdapter adapter = getTestSyncAdapter(CalendarSyncAdapter.class);
    149         EasCalendarSyncParser p = adapter.new EasCalendarSyncParser(getTestInputStream(), adapter);
    150         ContentValues cv = new ContentValues();
    151         GregorianCalendar startCalendar = new GregorianCalendar(2010, 5, 10, 8, 30);
    152         Long startTime = startCalendar.getTimeInMillis();
    153         GregorianCalendar endCalendar = new GregorianCalendar(2010, 5, 11, 8, 30);
    154         Long endTime = endCalendar.getTimeInMillis();
    155         cv.put(Events.RRULE, "FREQ=WEEKLY;BYDAY=MO");
    156         p.setTimeRelatedValues(cv, startTime, endTime, 1);
    157 
    158         // The start time should have hour/min/sec zero'd out
    159         startCalendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
    160         startCalendar.set(2010, 5, 10, 0, 0, 0);
    161         startCalendar.set(GregorianCalendar.MILLISECOND, 0);
    162         startTime = startCalendar.getTimeInMillis();
    163         assertEquals(startTime, cv.getAsLong(Events.DTSTART));
    164 
    165         // The duration should be in days
    166         assertEquals("P1D", cv.getAsString(Events.DURATION));
    167         assertNull(cv.getAsLong(Events.DTEND));
    168         assertNull(cv.getAsLong(Events.LAST_DATE));
    169         // There must be a timezone
    170         assertNotNull(cv.getAsString(Events.EVENT_TIMEZONE));
    171     }
    172 
    173     public void testSetTimeRelatedValues_Recurring_AllDay_Exception () throws IOException {
    174         CalendarSyncAdapter adapter = getTestSyncAdapter(CalendarSyncAdapter.class);
    175         EasCalendarSyncParser p = adapter.new EasCalendarSyncParser(getTestInputStream(), adapter);
    176         ContentValues cv = new ContentValues();
    177 
    178         // Recurrence exception for all-day event; the exception is NOT all-day
    179         GregorianCalendar startCalendar = new GregorianCalendar(2010, 5, 17, 8, 30);
    180         Long startTime = startCalendar.getTimeInMillis();
    181         GregorianCalendar endCalendar = new GregorianCalendar(2010, 5, 17, 9, 30);
    182         Long endTime = endCalendar.getTimeInMillis();
    183         cv.put(Events.ORIGINAL_ALL_DAY, 1);
    184         GregorianCalendar instanceCalendar = new GregorianCalendar(2010, 5, 17, 8, 30);
    185         cv.put(Events.ORIGINAL_INSTANCE_TIME, instanceCalendar.getTimeInMillis());
    186         p.setTimeRelatedValues(cv, startTime, endTime, 0);
    187 
    188         // The original instance time should have hour/min/sec zero'd out
    189         GregorianCalendar testCalendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
    190         testCalendar.set(2010, 5, 17, 0, 0, 0);
    191         testCalendar.set(GregorianCalendar.MILLISECOND, 0);
    192         Long testTime = testCalendar.getTimeInMillis();
    193         assertEquals(testTime, cv.getAsLong(Events.ORIGINAL_INSTANCE_TIME));
    194 
    195         // The exception isn't all-day, so we should have DTEND and LAST_DATE and no EVENT_TIMEZONE
    196         assertNull(cv.getAsString(Events.DURATION));
    197         assertEquals(endTime, cv.getAsLong(Events.DTEND));
    198         assertEquals(endTime, cv.getAsLong(Events.LAST_DATE));
    199         assertNull(cv.getAsString(Events.EVENT_TIMEZONE));
    200     }
    201 
    202     public void testIsValidEventValues() throws IOException {
    203         CalendarSyncAdapter adapter = getTestSyncAdapter(CalendarSyncAdapter.class);
    204         EasCalendarSyncParser p = adapter.new EasCalendarSyncParser(getTestInputStream(), adapter);
    205 
    206         long validTime = System.currentTimeMillis();
    207         String validData = "foo-bar-bletch";
    208         String validDuration = "P30M";
    209         String validRrule = "FREQ=DAILY";
    210 
    211         ContentValues cv = new ContentValues();
    212 
    213         cv.put(Events.DTSTART, validTime);
    214         // Needs _SYNC_DATA and DTEND/DURATION
    215         assertFalse(p.isValidEventValues(cv));
    216         cv.put(Events.SYNC_DATA2, validData);
    217         // Needs DTEND/DURATION since not an exception
    218         assertFalse(p.isValidEventValues(cv));
    219         cv.put(Events.DURATION, validDuration);
    220         // Valid (DTSTART, _SYNC_DATA, DURATION)
    221         assertTrue(p.isValidEventValues(cv));
    222         cv.remove(Events.DURATION);
    223         cv.put(Events.ORIGINAL_INSTANCE_TIME, validTime);
    224         // Needs DTEND since it's an exception
    225         assertFalse(p.isValidEventValues(cv));
    226         cv.put(Events.DTEND, validTime);
    227         // Valid (DTSTART, DTEND, ORIGINAL_INSTANCE_TIME)
    228         cv.remove(Events.ORIGINAL_INSTANCE_TIME);
    229         // Valid (DTSTART, _SYNC_DATA, DTEND)
    230         assertTrue(p.isValidEventValues(cv));
    231         cv.remove(Events.DTSTART);
    232         // Needs DTSTART
    233         assertFalse(p.isValidEventValues(cv));
    234         cv.put(Events.DTSTART, validTime);
    235         cv.put(Events.RRULE, validRrule);
    236         // With RRULE, needs DURATION
    237         assertFalse(p.isValidEventValues(cv));
    238         cv.put(Events.DURATION, "P30M");
    239         // Valid (DTSTART, RRULE, DURATION)
    240         assertTrue(p.isValidEventValues(cv));
    241         cv.put(Events.ALL_DAY, "1");
    242         // Needs DURATION in the form P<n>D
    243         assertFalse(p.isValidEventValues(cv));
    244         // Valid (DTSTART, RRULE, ALL_DAY, DURATION(P<n>D)
    245         cv.put(Events.DURATION, "P1D");
    246         assertTrue(p.isValidEventValues(cv));
    247     }
    248 
    249     private void addAttendeesToSerializer(Serializer s, int num) throws IOException {
    250         for (int i = 0; i < num; i++) {
    251             s.start(Tags.CALENDAR_ATTENDEE);
    252             s.data(Tags.CALENDAR_ATTENDEE_EMAIL, "frederick" + num +
    253                     ".flintstone (at) this.that.verylongservername.com");
    254             s.data(Tags.CALENDAR_ATTENDEE_TYPE, "1");
    255             s.data(Tags.CALENDAR_ATTENDEE_NAME, "Frederick" + num + " Flintstone, III");
    256             s.end();
    257         }
    258     }
    259 
    260     private void addAttendeeToSerializer(Serializer s, String email, String name)
    261             throws IOException {
    262         s.start(Tags.CALENDAR_ATTENDEE);
    263         s.data(Tags.CALENDAR_ATTENDEE_EMAIL, email);
    264         s.data(Tags.CALENDAR_ATTENDEE_TYPE, "1");
    265         s.data(Tags.CALENDAR_ATTENDEE_NAME, name);
    266         s.end();
    267     }
    268 
    269     private int countInsertOperationsForTable(CalendarOperations ops, String tableName) {
    270         int cnt = 0;
    271         for (Operation op: ops) {
    272             ContentProviderOperation cpo =
    273                     AbstractSyncAdapter.operationToContentProviderOperation(op, 0);
    274             List<String> segments = cpo.getUri().getPathSegments();
    275             if (segments.get(0).equalsIgnoreCase(tableName) &&
    276                     cpo.getType() == ContentProviderOperation.TYPE_INSERT) {
    277                 cnt++;
    278             }
    279         }
    280         return cnt;
    281     }
    282 
    283     class TestEvent extends Serializer {
    284         CalendarSyncAdapter mAdapter;
    285         EasCalendarSyncParser mParser;
    286         Serializer mSerializer;
    287 
    288         TestEvent() throws IOException {
    289             super(false);
    290             mAdapter = getTestSyncAdapter(CalendarSyncAdapter.class);
    291             mParser = mAdapter.new EasCalendarSyncParser(getTestInputStream(), mAdapter);
    292         }
    293 
    294         void setUserEmailAddress(String addr) {
    295             mAdapter.mAccount.mEmailAddress = addr;
    296             mAdapter.mEmailAddress = addr;
    297         }
    298 
    299         EasCalendarSyncParser getParser() throws IOException {
    300             // Set up our parser's input and eat the initial tag
    301             mParser.resetInput(new ByteArrayInputStream(toByteArray()));
    302             mParser.nextTag(0);
    303             return mParser;
    304         }
    305 
    306         // setupPreAttendees and setupPostAttendees initialize calendar data in the order in which
    307         // they would appear in an actual EAS session.  Between these two calls, we initialize
    308         // attendee data, which varies between the following tests
    309         TestEvent setupPreAttendees() throws IOException {
    310             start(Tags.SYNC_APPLICATION_DATA);
    311             data(Tags.CALENDAR_TIME_ZONE, TEST_TIME_ZONE);
    312             data(Tags.CALENDAR_DTSTAMP, "20100518T213156Z");
    313             data(Tags.CALENDAR_START_TIME, "20100518T220000Z");
    314             data(Tags.CALENDAR_SUBJECT, "Documentation");
    315             data(Tags.CALENDAR_UID, "4417556B-27DE-4ECE-B679-A63EFE1F9E85");
    316             data(Tags.CALENDAR_ORGANIZER_NAME, "Fred Squatibuquitas");
    317             data(Tags.CALENDAR_ORGANIZER_EMAIL, "fred.squatibuquitas (at) prettylongdomainname.com");
    318             return this;
    319         }
    320 
    321         TestEvent setupPostAttendees()throws IOException {
    322             data(Tags.CALENDAR_LOCATION, "CR SF 601T2/North Shore Presentation Self Service (16)");
    323             data(Tags.CALENDAR_END_TIME, "20100518T223000Z");
    324             start(Tags.BASE_BODY);
    325             data(Tags.BASE_BODY_PREFERENCE, "1");
    326             data(Tags.BASE_ESTIMATED_DATA_SIZE, "69105"); // The number is ignored by the parser
    327             data(Tags.BASE_DATA,
    328                     "This is the event description; we should probably make it longer");
    329             end(); // BASE_BODY
    330             start(Tags.CALENDAR_RECURRENCE);
    331             data(Tags.CALENDAR_RECURRENCE_TYPE, "1"); // weekly
    332             data(Tags.CALENDAR_RECURRENCE_INTERVAL, "1");
    333             data(Tags.CALENDAR_RECURRENCE_OCCURRENCES, "10");
    334             data(Tags.CALENDAR_RECURRENCE_DAYOFWEEK, "12"); // tue, wed
    335             data(Tags.CALENDAR_RECURRENCE_UNTIL, "2005-04-14T00:00:00.000Z");
    336             end();  // CALENDAR_RECURRENCE
    337             data(Tags.CALENDAR_SENSITIVITY, "0");
    338             data(Tags.CALENDAR_BUSY_STATUS, "2");
    339             data(Tags.CALENDAR_ALL_DAY_EVENT, "0");
    340             data(Tags.CALENDAR_MEETING_STATUS, "3");
    341             data(Tags.BASE_NATIVE_BODY_TYPE, "3");
    342             end().done(); // SYNC_APPLICATION_DATA
    343             return this;
    344         }
    345     }
    346 
    347     public void testAddEvent() throws IOException {
    348         TestEvent event = new TestEvent();
    349         event.setupPreAttendees();
    350         event.start(Tags.CALENDAR_ATTENDEES);
    351         addAttendeesToSerializer(event, 10);
    352         event.end(); // CALENDAR_ATTENDEES
    353         event.setupPostAttendees();
    354 
    355         EasCalendarSyncParser p = event.getParser();
    356         p.addEvent(p.mOps, "1:1", false);
    357         // There should be 1 event
    358         assertEquals(1, countInsertOperationsForTable(p.mOps, "events"));
    359         // Two attendees (organizer and 10 attendees)
    360         assertEquals(11, countInsertOperationsForTable(p.mOps, "attendees"));
    361         // dtstamp, meeting status, attendees, attendees redacted, and upsync prohibited
    362         assertEquals(5, countInsertOperationsForTable(p.mOps, "extendedproperties"));
    363     }
    364 
    365     public void testAddEventIllegal() throws IOException {
    366         // We don't send a start time; the event is illegal and nothing should be added
    367         TestEvent event = new TestEvent();
    368         event.start(Tags.SYNC_APPLICATION_DATA);
    369         event.data(Tags.CALENDAR_TIME_ZONE, TEST_TIME_ZONE);
    370         event.data(Tags.CALENDAR_DTSTAMP, "20100518T213156Z");
    371         event.data(Tags.CALENDAR_SUBJECT, "Documentation");
    372         event.data(Tags.CALENDAR_UID, "4417556B-27DE-4ECE-B679-A63EFE1F9E85");
    373         event.data(Tags.CALENDAR_ORGANIZER_NAME, "Fred Squatibuquitas");
    374         event.data(Tags.CALENDAR_ORGANIZER_EMAIL, "fred.squatibuquitas (at) prettylongdomainname.com");
    375         event.start(Tags.CALENDAR_ATTENDEES);
    376         addAttendeesToSerializer(event, 10);
    377         event.end(); // CALENDAR_ATTENDEES
    378         event.setupPostAttendees();
    379 
    380         EasCalendarSyncParser p = event.getParser();
    381         p.addEvent(p.mOps, "1:1", false);
    382         assertEquals(0, countInsertOperationsForTable(p.mOps, "events"));
    383         assertEquals(0, countInsertOperationsForTable(p.mOps, "attendees"));
    384         assertEquals(0, countInsertOperationsForTable(p.mOps, "extendedproperties"));
    385     }
    386 
    387     public void testAddEventRedactedAttendees() throws IOException {
    388         TestEvent event = new TestEvent();
    389         event.setupPreAttendees();
    390         event.start(Tags.CALENDAR_ATTENDEES);
    391         addAttendeesToSerializer(event, 100);
    392         event.end(); // CALENDAR_ATTENDEES
    393         event.setupPostAttendees();
    394 
    395         EasCalendarSyncParser p = event.getParser();
    396         p.addEvent(p.mOps, "1:1", false);
    397         // There should be 1 event
    398         assertEquals(1, countInsertOperationsForTable(p.mOps, "events"));
    399         // One attendees (organizer; all others are redacted)
    400         assertEquals(1, countInsertOperationsForTable(p.mOps, "attendees"));
    401         // dtstamp, meeting status, and attendees redacted
    402         assertEquals(3, countInsertOperationsForTable(p.mOps, "extendedproperties"));
    403     }
    404 
    405     /**
    406      * Setup for the following three tests, which check attendee status of an added event
    407      * @param userEmail the email address of the user
    408      * @param update whether or not the event is an update (rather than new)
    409      * @return a Cursor to the Attendee records added to our MockProvider
    410      * @throws IOException
    411      * @throws RemoteException
    412      * @throws OperationApplicationException
    413      */
    414     private Cursor setupAddEventOneAttendee(String userEmail, boolean update)
    415             throws IOException, RemoteException, OperationApplicationException {
    416         TestEvent event = new TestEvent();
    417         event.setupPreAttendees();
    418         event.start(Tags.CALENDAR_ATTENDEES);
    419         addAttendeeToSerializer(event, SINGLE_ATTENDEE_EMAIL, SINGLE_ATTENDEE_NAME);
    420         event.setUserEmailAddress(userEmail);
    421         event.end(); // CALENDAR_ATTENDEES
    422         event.setupPostAttendees();
    423 
    424         EasCalendarSyncParser p = event.getParser();
    425         p.addEvent(p.mOps, "1:1", update);
    426         // Send the CPO's to the mock provider
    427         ArrayList<ContentProviderOperation> cpos = new ArrayList<ContentProviderOperation>();
    428         for (Operation op: p.mOps) {
    429             cpos.add(AbstractSyncAdapter.operationToContentProviderOperation(op, 0));
    430         }
    431         mMockResolver.applyBatch(MockProvider.AUTHORITY, cpos);
    432         return mMockResolver.query(MockProvider.uri(Attendees.CONTENT_URI), ATTENDEE_PROJECTION,
    433                 null, null, null);
    434     }
    435 
    436     public void testAddEventOneAttendee() throws IOException, RemoteException,
    437             OperationApplicationException {
    438         Cursor c = setupAddEventOneAttendee("foo (at) bar.com", false);
    439         assertEquals(2, c.getCount());
    440         // The organizer should be "accepted", the unknown attendee "none"
    441         while (c.moveToNext()) {
    442             if (SINGLE_ATTENDEE_EMAIL.equals(c.getString(ATTENDEE_EMAIL))) {
    443                 assertEquals(Attendees.ATTENDEE_STATUS_NONE, c.getInt(ATTENDEE_STATUS));
    444             } else {
    445                 assertEquals(Attendees.ATTENDEE_STATUS_ACCEPTED, c.getInt(ATTENDEE_STATUS));
    446             }
    447         }
    448     }
    449 
    450     public void testAddEventSelfAttendee() throws IOException, RemoteException,
    451             OperationApplicationException {
    452         Cursor c = setupAddEventOneAttendee(SINGLE_ATTENDEE_EMAIL, false);
    453         // The organizer should be "accepted", and our user/attendee should be "done" even though
    454         // the busy status = 2 (because we can't tell from a status of 2 on new events)
    455         while (c.moveToNext()) {
    456             if (SINGLE_ATTENDEE_EMAIL.equals(c.getString(ATTENDEE_EMAIL))) {
    457                 assertEquals(Attendees.ATTENDEE_STATUS_NONE, c.getInt(ATTENDEE_STATUS));
    458             } else {
    459                 assertEquals(Attendees.ATTENDEE_STATUS_ACCEPTED, c.getInt(ATTENDEE_STATUS));
    460             }
    461         }
    462     }
    463 
    464     public void testAddEventSelfAttendeeUpdate() throws IOException, RemoteException,
    465             OperationApplicationException {
    466         Cursor c = setupAddEventOneAttendee(SINGLE_ATTENDEE_EMAIL, true);
    467         // The organizer should be "accepted", and our user/attendee should be "accepted" (because
    468         // busy status = 2 and this is an update
    469         while (c.moveToNext()) {
    470             if (SINGLE_ATTENDEE_EMAIL.equals(c.getString(ATTENDEE_EMAIL))) {
    471                 assertEquals(Attendees.ATTENDEE_STATUS_ACCEPTED, c.getInt(ATTENDEE_STATUS));
    472             } else {
    473                 assertEquals(Attendees.ATTENDEE_STATUS_ACCEPTED, c.getInt(ATTENDEE_STATUS));
    474             }
    475         }
    476     }
    477 }
    478