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; 18 19 import android.accounts.Account; 20 import android.accounts.AccountManager; 21 import android.accounts.AccountManagerFuture; 22 import android.accounts.AuthenticatorException; 23 import android.accounts.OperationCanceledException; 24 import android.app.NotificationManager; 25 import android.content.ContentResolver; 26 import android.content.Context; 27 import android.database.Cursor; 28 import android.provider.CalendarContract; 29 import android.test.MoreAsserts; 30 import android.test.suitebuilder.annotation.MediumTest; 31 import android.text.TextUtils; 32 33 import com.android.emailcommon.Logging; 34 import com.android.exchange.utility.ExchangeTestCase; 35 import com.android.mail.utils.LogUtils; 36 37 import java.io.IOException; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.HashMap; 41 import java.util.HashSet; 42 43 @MediumTest 44 public class CalendarSyncEnablerTest extends ExchangeTestCase { 45 46 protected static final String TEST_ACCOUNT_PREFIX = "__test"; 47 protected static final String TEST_ACCOUNT_SUFFIX = "@android.com"; 48 49 private HashMap<Account, Boolean> origCalendarSyncStates = new HashMap<Account, Boolean>(); 50 51 // To make the rest of the code shorter thus more readable... 52 private static final String EAT = "com.android.exchange"; 53 54 @Override 55 public void setUp() throws Exception { 56 super.setUp(); 57 // Delete any test accounts we might have created earlier 58 deleteTemporaryAccountManagerAccounts(); 59 60 // Save the original calendar sync states. 61 for (Account account : AccountManager.get(getContext()).getAccounts()) { 62 origCalendarSyncStates.put(account, 63 ContentResolver.getSyncAutomatically(account, CalendarContract.AUTHORITY)); 64 } 65 } 66 67 @Override 68 public void tearDown() throws Exception { 69 super.tearDown(); 70 // Delete any test accounts we might have created earlier 71 deleteTemporaryAccountManagerAccounts(); 72 73 // Restore the original calendar sync states. 74 // Note we restore only for Exchange accounts. 75 // Other accounts should remain intact throughout the tests. Plus we don't know if the 76 // Calendar.AUTHORITY is supported by other types of accounts. 77 for (Account account : getExchangeAccounts()) { 78 Boolean state = origCalendarSyncStates.get(account); 79 if (state == null) continue; // Shouldn't happen, but just in case. 80 81 ContentResolver.setSyncAutomatically(account, CalendarContract.AUTHORITY, state); 82 } 83 } 84 85 public void brokentestEnableEasCalendarSync() { 86 final Account[] baseAccounts = getExchangeAccounts(); 87 88 String a1 = getTestAccountEmailAddress("1"); 89 String a2 = getTestAccountEmailAddress("2"); 90 91 // 1. Test with 1 account 92 93 CalendarSyncEnabler enabler = new CalendarSyncEnabler(getContext()); 94 95 // Add exchange accounts 96 createAccountManagerAccount(a1); 97 98 String emailAddresses = enabler.enableEasCalendarSyncInternalForTest(); 99 100 // Verify 101 verifyCalendarSyncState(); 102 103 // There seems to be no good way to examine the contents of Notification, so let's verify 104 // we at least (tried to) show the correct email addresses. 105 checkNotificationEmailAddresses(emailAddresses, baseAccounts, a1); 106 107 // Delete added account. 108 deleteTemporaryAccountManagerAccounts(); 109 110 // 2. Test with 2 accounts 111 enabler = new CalendarSyncEnabler(getContext()); 112 113 // Add exchange accounts 114 createAccountManagerAccount(a1); 115 createAccountManagerAccount(a2); 116 117 emailAddresses = enabler.enableEasCalendarSyncInternalForTest(); 118 119 // Verify 120 verifyCalendarSyncState(); 121 122 // Check 123 checkNotificationEmailAddresses(emailAddresses, baseAccounts, a1, a2); 124 } 125 126 private static void checkNotificationEmailAddresses(String actual, Account[] baseAccounts, 127 String... addedAddresses) { 128 // Build and sort actual string array. 129 final String[] actualArray = TextUtils.split(actual, " "); 130 Arrays.sort(actualArray); 131 132 // Build and sort expected string array. 133 ArrayList<String> expected = new ArrayList<String>(); 134 for (Account account : baseAccounts) { 135 expected.add(account.name); 136 } 137 for (String address : addedAddresses) { 138 expected.add(address); 139 } 140 final String[] expectedArray = new String[expected.size()]; 141 expected.toArray(expectedArray); 142 Arrays.sort(expectedArray); 143 144 // Check! 145 MoreAsserts.assertEquals(expectedArray, actualArray); 146 } 147 148 /** 149 * For all {@link Account}, confirm that: 150 * <ol> 151 * <li>Calendar sync is enabled if it's an Exchange account.<br> 152 * Unfortunately setSyncAutomatically() doesn't take effect immediately, so we skip this 153 * check for now. 154 TODO Find a stable way to check this. 155 * <li>Otherwise, calendar sync state isn't changed. 156 * </ol> 157 */ 158 private void verifyCalendarSyncState() { 159 // It's very unfortunate that setSyncAutomatically doesn't take effect immediately. 160 for (Account account : AccountManager.get(getContext()).getAccounts()) { 161 String message = "account=" + account.name + "(" + account.type + ")"; 162 boolean enabled = ContentResolver.getSyncAutomatically(account, 163 CalendarContract.AUTHORITY); 164 int syncable = ContentResolver.getIsSyncable(account, CalendarContract.AUTHORITY); 165 166 if (EAT.equals(account.type)) { 167 // Should be enabled. 168 // assertEquals(message, Boolean.TRUE, (Boolean) enabled); 169 // assertEquals(message, 1, syncable); 170 } else { 171 // Shouldn't change. 172 assertEquals(message, origCalendarSyncStates.get(account), (Boolean) enabled); 173 } 174 } 175 } 176 177 public void testEnableEasCalendarSyncWithNoExchangeAccounts() { 178 // This test can only meaningfully run when there's no exchange accounts 179 // set up on the device. Otherwise there'll be no difference from 180 // testEnableEasCalendarSync. 181 if (AccountManager.get(getContext()).getAccountsByType(EAT).length > 0) { 182 LogUtils.w(Logging.LOG_TAG, "testEnableEasCalendarSyncWithNoExchangeAccounts skipped:" 183 + " It only runs when there's no Exchange account on the device."); 184 return; 185 } 186 CalendarSyncEnabler enabler = new CalendarSyncEnabler(getContext()); 187 String emailAddresses = enabler.enableEasCalendarSyncInternalForTest(); 188 189 // Verify (nothing should change) 190 verifyCalendarSyncState(); 191 192 // No exchange accounts found. 193 assertEquals(0, emailAddresses.length()); 194 } 195 196 public void testShowNotification() { 197 CalendarSyncEnabler enabler = new CalendarSyncEnabler(getContext()); 198 199 // We can't really check the result, but at least we can make sure it won't crash.... 200 enabler.showNotificationForTest("a (at) b.com"); 201 202 // Remove the notification. Comment it out when you want to know how it looks like. 203 // TODO If NotificationController supports this notification, we can just mock it out 204 // and remove this code. 205 ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)) 206 .cancel(CalendarSyncEnabler.NOTIFICATION_ID_EXCHANGE_CALENDAR_ADDED); 207 } 208 209 protected Account[] getExchangeAccounts() { 210 return AccountManager.get(getContext()).getAccountsByType(EAT); 211 } 212 213 protected Account makeAccountManagerAccount(String username) { 214 return new Account(username, EAT); 215 } 216 217 protected void createAccountManagerAccount(String username) { 218 final Account account = makeAccountManagerAccount(username); 219 AccountManager.get(getContext()).addAccountExplicitly(account, "password", null); 220 } 221 222 protected com.android.emailcommon.provider.Account 223 setupProviderAndAccountManagerAccount(String username) { 224 // Note that setupAccount creates the email address username (at) android.com, so that's what 225 // we need to use for the account manager 226 createAccountManagerAccount(username + TEST_ACCOUNT_SUFFIX); 227 return setupTestAccount(username, true); 228 } 229 230 protected ArrayList<com.android.emailcommon.provider.Account> makeExchangeServiceAccountList() { 231 ArrayList<com.android.emailcommon.provider.Account> accountList = 232 new ArrayList<com.android.emailcommon.provider.Account>(); 233 Cursor c = mProviderContext.getContentResolver().query( 234 com.android.emailcommon.provider.Account.CONTENT_URI, 235 com.android.emailcommon.provider.Account.CONTENT_PROJECTION, null, null, null); 236 try { 237 while (c.moveToNext()) { 238 com.android.emailcommon.provider.Account account = 239 new com.android.emailcommon.provider.Account(); 240 account.restore(c); 241 accountList.add(account); 242 } 243 } finally { 244 c.close(); 245 } 246 return accountList; 247 } 248 249 protected void deleteAccountManagerAccount(Account account) { 250 AccountManagerFuture<Boolean> future = 251 AccountManager.get(getContext()).removeAccount(account, null, null); 252 try { 253 future.getResult(); 254 } catch (OperationCanceledException e) { 255 } catch (AuthenticatorException e) { 256 } catch (IOException e) { 257 } 258 } 259 260 protected void deleteTemporaryAccountManagerAccounts() { 261 for (Account accountManagerAccount: getExchangeAccounts()) { 262 if (accountManagerAccount.name.startsWith(TEST_ACCOUNT_PREFIX) && 263 accountManagerAccount.name.endsWith(TEST_ACCOUNT_SUFFIX)) { 264 deleteAccountManagerAccount(accountManagerAccount); 265 } 266 } 267 } 268 269 protected String getTestAccountName(String name) { 270 return TEST_ACCOUNT_PREFIX + name; 271 } 272 273 protected String getTestAccountEmailAddress(String name) { 274 return TEST_ACCOUNT_PREFIX + name + TEST_ACCOUNT_SUFFIX; 275 } 276 277 278 /** 279 * Helper to retrieve account manager accounts *and* remove any preexisting accounts 280 * from the list, to "hide" them from the reconciler. 281 */ 282 protected Account[] getAccountManagerAccounts(Account[] baseline) { 283 Account[] rawList = getExchangeAccounts(); 284 if (baseline.length == 0) { 285 return rawList; 286 } 287 HashSet<Account> set = new HashSet<Account>(); 288 for (Account addAccount : rawList) { 289 set.add(addAccount); 290 } 291 for (Account removeAccount : baseline) { 292 set.remove(removeAccount); 293 } 294 return set.toArray(new Account[0]); 295 } 296 } 297