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