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.email.service; 18 19 import android.accounts.AccountManager; 20 import android.content.ComponentName; 21 import android.content.ContentResolver; 22 import android.content.ContentUris; 23 import android.content.ContentValues; 24 import android.content.Context; 25 import android.content.pm.PackageManager; 26 27 import com.android.email.AccountTestCase; 28 import com.android.email.Controller; 29 import com.android.email.provider.AccountReconciler; 30 import com.android.email.provider.EmailProvider; 31 import com.android.email.provider.ProviderTestUtils; 32 import com.android.email.service.MailService.AccountSyncReport; 33 import com.android.emailcommon.provider.Account; 34 import com.android.emailcommon.provider.EmailContent; 35 import com.android.emailcommon.provider.HostAuth; 36 37 import java.util.ArrayList; 38 import java.util.HashMap; 39 import java.util.List; 40 41 /** 42 * Tests of the Email provider. 43 * 44 * You can run this entire test case with: 45 * runtest -c com.android.email.service.MailServiceTests email 46 */ 47 public class MailServiceTests extends AccountTestCase { 48 49 EmailProvider mProvider; 50 Context mMockContext; 51 52 public MailServiceTests() { 53 super(); 54 } 55 56 @Override 57 public void setUp() throws Exception { 58 super.setUp(); 59 PackageManager pm = getContext().getPackageManager(); 60 pm.setComponentEnabledSetting( 61 new ComponentName(getContext(), EasTestAuthenticatorService.class), 62 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 63 PackageManager.DONT_KILL_APP); 64 mMockContext = getMockContext(); 65 // Delete any test accounts we might have created earlier 66 deleteTemporaryAccountManagerAccounts(); 67 } 68 69 @Override 70 public void tearDown() throws Exception { 71 super.tearDown(); 72 // Delete any test accounts we might have created earlier 73 deleteTemporaryAccountManagerAccounts(); 74 } 75 76 /** 77 * Confirm that the test below is functional (and non-destructive) when there are 78 * prexisting (non-test) accounts in the account manager. 79 */ 80 public void testTestReconcileAccounts() { 81 Account firstAccount = null; 82 final String TEST_USER_ACCOUNT = "__user_account_test_1"; 83 Context context = getContext(); 84 try { 85 // Note: Unlike calls to setupProviderAndAccountManagerAccount(), we are creating 86 // *real* accounts here (not in the mock provider) 87 createAccountManagerAccount(TEST_USER_ACCOUNT + TEST_ACCOUNT_SUFFIX); 88 firstAccount = ProviderTestUtils.setupAccount(TEST_USER_ACCOUNT, true, context); 89 // Now run the test with the "user" accounts in place 90 testReconcileAccounts(); 91 } finally { 92 if (firstAccount != null) { 93 boolean firstAccountFound = false; 94 // delete the provider account 95 context.getContentResolver().delete(firstAccount.getUri(), null, null); 96 // delete the account manager account 97 android.accounts.Account[] accountManagerAccounts = AccountManager.get(context) 98 .getAccountsByType(TEST_ACCOUNT_TYPE); 99 for (android.accounts.Account accountManagerAccount: accountManagerAccounts) { 100 if ((TEST_USER_ACCOUNT + TEST_ACCOUNT_SUFFIX) 101 .equals(accountManagerAccount.name)) { 102 deleteAccountManagerAccount(accountManagerAccount); 103 firstAccountFound = true; 104 } 105 } 106 assertTrue(firstAccountFound); 107 } 108 } 109 } 110 111 /** 112 * Note, there is some inherent risk in this test, as it creates *real* accounts in the 113 * system (it cannot use the mock context with the Account Manager). 114 */ 115 public void testReconcileAccounts() { 116 // Note that we can't use mMockContext for AccountManager interactions, as it isn't a fully 117 // functional Context. 118 Context context = getContext(); 119 120 // Capture the baseline (account manager accounts) so we can measure the changes 121 // we're making, irrespective of the number of actual accounts, and not destroy them 122 android.accounts.Account[] baselineAccounts = 123 AccountManager.get(context).getAccountsByType(TEST_ACCOUNT_TYPE); 124 125 // Set up three accounts, both in AccountManager and in EmailProvider 126 Account firstAccount = setupProviderAndAccountManagerAccount(getTestAccountName("1")); 127 setupProviderAndAccountManagerAccount(getTestAccountName("2")); 128 setupProviderAndAccountManagerAccount(getTestAccountName("3")); 129 130 // Check that they're set up properly 131 assertEquals(3, EmailContent.count(mMockContext, Account.CONTENT_URI, null, null)); 132 android.accounts.Account[] accountManagerAccounts = 133 getAccountManagerAccounts(baselineAccounts); 134 assertEquals(3, accountManagerAccounts.length); 135 136 // Delete account "2" from AccountManager 137 android.accounts.Account removedAccount = 138 makeAccountManagerAccount(getTestAccountEmailAddress("2")); 139 deleteAccountManagerAccount(removedAccount); 140 141 // Confirm it's deleted 142 accountManagerAccounts = getAccountManagerAccounts(baselineAccounts); 143 assertEquals(2, accountManagerAccounts.length); 144 145 // Run the reconciler 146 ContentResolver resolver = mMockContext.getContentResolver(); 147 MailService.reconcileAccountsWithAccountManager(context, 148 makeExchangeServiceAccountList(), accountManagerAccounts, mMockContext); 149 150 // There should now be only two EmailProvider accounts 151 assertEquals(2, EmailContent.count(mMockContext, Account.CONTENT_URI, null, null)); 152 153 // Ok, now we've got two of each; let's delete a provider account 154 resolver.delete(ContentUris.withAppendedId(Account.CONTENT_URI, firstAccount.mId), 155 null, null); 156 // ...and then there was one 157 assertEquals(1, EmailContent.count(mMockContext, Account.CONTENT_URI, null, null)); 158 159 // Run the reconciler 160 MailService.reconcileAccountsWithAccountManager(context, 161 makeExchangeServiceAccountList(), accountManagerAccounts, mMockContext); 162 163 // There should now be only one AccountManager account 164 accountManagerAccounts = getAccountManagerAccounts(baselineAccounts); 165 assertEquals(1, accountManagerAccounts.length); 166 // ... and it should be account "3" 167 assertEquals(getTestAccountEmailAddress("3"), accountManagerAccounts[0].name); 168 } 169 170 public void testReconcileDetection() { 171 Context context = getContext(); 172 List<Account> providerAccounts; 173 android.accounts.Account[] accountManagerAccounts; 174 175 android.accounts.Account[] baselineAccounts = 176 AccountManager.get(context).getAccountsByType(TEST_ACCOUNT_TYPE); 177 178 // Empty lists match. 179 providerAccounts = new ArrayList<Account>(); 180 accountManagerAccounts = new android.accounts.Account[0]; 181 assertFalse(AccountReconciler.accountsNeedReconciling( 182 context, providerAccounts, accountManagerAccounts)); 183 184 setupProviderAndAccountManagerAccount(getTestAccountName("1")); 185 accountManagerAccounts = getAccountManagerAccounts(baselineAccounts); 186 providerAccounts = makeExchangeServiceAccountList(); 187 188 // A single account, but empty list on the other side is detected as needing reconciliation 189 assertTrue(AccountReconciler.accountsNeedReconciling( 190 context, new ArrayList<Account>(), accountManagerAccounts)); 191 assertTrue(AccountReconciler.accountsNeedReconciling( 192 context, providerAccounts, new android.accounts.Account[0])); 193 194 // Note that no reconciliation should have happened though - we just wanted to detect it. 195 assertEquals(1, makeExchangeServiceAccountList().size()); 196 assertEquals(1, getAccountManagerAccounts(baselineAccounts).length); 197 198 // Single account matches - no reconciliation should be detected. 199 assertFalse(AccountReconciler.accountsNeedReconciling( 200 context, providerAccounts, accountManagerAccounts)); 201 202 // Provider: 1,2,3. AccountManager: 1, 3. 203 String username = getTestAccountName("2"); 204 ProviderTestUtils.setupAccount(getTestAccountName("2"), true, getMockContext()); 205 setupProviderAndAccountManagerAccount(getTestAccountName("3")); 206 207 accountManagerAccounts = getAccountManagerAccounts(baselineAccounts); 208 providerAccounts = makeExchangeServiceAccountList(); 209 assertTrue(AccountReconciler.accountsNeedReconciling( 210 context, providerAccounts, accountManagerAccounts)); 211 } 212 213 214 /** 215 * Lightweight subclass of the Controller class allows injection of mock context 216 */ 217 public static class TestController extends Controller { 218 219 protected TestController(Context providerContext, Context systemContext) { 220 super(systemContext); 221 setProviderContext(providerContext); 222 } 223 } 224 225 /** 226 * Create a simple HostAuth with protocol 227 */ 228 private HostAuth setupSimpleHostAuth(String protocol) { 229 HostAuth hostAuth = new HostAuth(); 230 hostAuth.mProtocol = protocol; 231 return hostAuth; 232 } 233 234 /** 235 * Initial testing on setupSyncReportsLocked, making sure that EAS accounts aren't scheduled 236 */ 237 public void testSetupSyncReportsLocked() { 238 // TODO Test other functionality within setupSyncReportsLocked 239 // Setup accounts of each type, all with manual sync at different intervals 240 Account easAccount = ProviderTestUtils.setupAccount("account1", false, mMockContext); 241 easAccount.mHostAuthRecv = setupSimpleHostAuth("eas"); 242 easAccount.mHostAuthSend = easAccount.mHostAuthRecv; 243 easAccount.mSyncInterval = 30; 244 easAccount.save(mMockContext); 245 Account imapAccount = ProviderTestUtils.setupAccount("account2", false, mMockContext); 246 imapAccount.mHostAuthRecv = setupSimpleHostAuth("imap"); 247 imapAccount.mHostAuthSend = setupSimpleHostAuth("smtp"); 248 imapAccount.mSyncInterval = 60; 249 imapAccount.save(mMockContext); 250 Account pop3Account = ProviderTestUtils.setupAccount("account3", false, mMockContext); 251 pop3Account.mHostAuthRecv = setupSimpleHostAuth("pop3"); 252 pop3Account.mHostAuthSend = setupSimpleHostAuth("smtp"); 253 pop3Account.mSyncInterval = 90; 254 pop3Account.save(mMockContext); 255 256 // Setup the SyncReport's for these Accounts 257 MailService mailService = new MailService(); 258 mailService.mController = new TestController(mMockContext, getContext()); 259 try { 260 mailService.setupSyncReportsLocked(MailService.SYNC_REPORTS_RESET, mMockContext); 261 262 // Get back the map created by MailService 263 HashMap<Long, AccountSyncReport> syncReportMap = MailService.mSyncReports; 264 synchronized (syncReportMap) { 265 // Check the SyncReport's for correctness of sync interval 266 AccountSyncReport syncReport = syncReportMap.get(easAccount.mId); 267 assertNotNull(syncReport); 268 // EAS sync interval should have been changed to "never" 269 assertEquals(Account.CHECK_INTERVAL_NEVER, syncReport.syncInterval); 270 syncReport = syncReportMap.get(imapAccount.mId); 271 assertNotNull(syncReport); 272 assertEquals(60, syncReport.syncInterval); 273 syncReport = syncReportMap.get(pop3Account.mId); 274 assertNotNull(syncReport); 275 assertEquals(90, syncReport.syncInterval); 276 // Change the EAS account to push 277 ContentValues cv = new ContentValues(); 278 cv.put(Account.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH); 279 easAccount.update(mMockContext, cv); 280 syncReportMap.clear(); 281 mailService.setupSyncReportsLocked(easAccount.mId, mMockContext); 282 syncReport = syncReportMap.get(easAccount.mId); 283 assertNotNull(syncReport); 284 // EAS sync interval should be "never" in this case as well 285 assertEquals(Account.CHECK_INTERVAL_NEVER, syncReport.syncInterval); 286 } 287 } finally { 288 mailService.mController.cleanupForTest(); 289 } 290 } 291 292 /** 293 * Test that setupSyncReports will skip over poorly-formed accounts which can be left 294 * over after unit tests. 295 */ 296 public void testSetupSyncReportsWithBadAccounts() { 297 // Setup accounts that trigger each skip-over case 298 // 1: no email address 299 Account account1 = ProviderTestUtils.setupAccount("account1", false, mMockContext); 300 account1.mHostAuthRecv = setupSimpleHostAuth("imap"); 301 account1.mHostAuthSend = setupSimpleHostAuth("smtp"); 302 account1.mSyncInterval = 30; 303 account1.mEmailAddress = null; 304 account1.save(mMockContext); 305 // 2: no receiver hostauth 306 Account account2 = ProviderTestUtils.setupAccount("account2", false, mMockContext); 307 account2.mHostAuthRecv = null; 308 account2.mHostAuthSend = setupSimpleHostAuth("smtp"); 309 account2.mSyncInterval = 30; 310 account2.save(mMockContext); 311 // 3: no sender hostauth 312 Account account3 = ProviderTestUtils.setupAccount("account3", false, mMockContext); 313 account3.mHostAuthRecv = setupSimpleHostAuth("imap"); 314 account3.mHostAuthSend = null; 315 account3.mSyncInterval = 30; 316 account3.save(mMockContext); 317 318 // Setup the SyncReport's for these Accounts 319 MailService mailService = new MailService(); 320 mailService.mController = new TestController(mMockContext, getContext()); 321 try { 322 mailService.setupSyncReportsLocked(MailService.SYNC_REPORTS_RESET, mMockContext); 323 // Get back the map created by MailService - it should be empty 324 HashMap<Long, AccountSyncReport> syncReportMap = MailService.mSyncReports; 325 assertEquals(0, syncReportMap.size()); 326 } finally { 327 mailService.mController.cleanupForTest(); 328 } 329 330 } 331 } 332