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 17package com.android.email.service; 18 19import android.accounts.AccountManager; 20import android.content.ComponentName; 21import android.content.ContentResolver; 22import android.content.ContentUris; 23import android.content.ContentValues; 24import android.content.Context; 25import android.content.pm.PackageManager; 26 27import com.android.email.AccountTestCase; 28import com.android.email.Controller; 29import com.android.email.provider.AccountReconciler; 30import com.android.email.provider.EmailProvider; 31import com.android.email.provider.ProviderTestUtils; 32import com.android.email.service.MailService.AccountSyncReport; 33import com.android.emailcommon.provider.Account; 34import com.android.emailcommon.provider.EmailContent; 35import com.android.emailcommon.provider.HostAuth; 36 37import java.util.ArrayList; 38import java.util.HashMap; 39import 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 */ 47public 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 /** 171 * Lightweight subclass of the Controller class allows injection of mock context 172 */ 173 public static class TestController extends Controller { 174 175 protected TestController(Context providerContext, Context systemContext) { 176 super(systemContext); 177 setProviderContext(providerContext); 178 } 179 } 180 181 /** 182 * Create a simple HostAuth with protocol 183 */ 184 private HostAuth setupSimpleHostAuth(String protocol) { 185 HostAuth hostAuth = new HostAuth(); 186 hostAuth.mProtocol = protocol; 187 return hostAuth; 188 } 189 190 /** 191 * Initial testing on setupSyncReportsLocked, making sure that EAS accounts aren't scheduled 192 */ 193 public void testSetupSyncReportsLocked() { 194 // TODO Test other functionality within setupSyncReportsLocked 195 // Setup accounts of each type, all with manual sync at different intervals 196 Account easAccount = ProviderTestUtils.setupAccount("account1", false, mMockContext); 197 easAccount.mHostAuthRecv = setupSimpleHostAuth("eas"); 198 easAccount.mHostAuthSend = easAccount.mHostAuthRecv; 199 easAccount.mSyncInterval = 30; 200 easAccount.save(mMockContext); 201 Account imapAccount = ProviderTestUtils.setupAccount("account2", false, mMockContext); 202 imapAccount.mHostAuthRecv = setupSimpleHostAuth("imap"); 203 imapAccount.mHostAuthSend = setupSimpleHostAuth("smtp"); 204 imapAccount.mSyncInterval = 60; 205 imapAccount.save(mMockContext); 206 Account pop3Account = ProviderTestUtils.setupAccount("account3", false, mMockContext); 207 pop3Account.mHostAuthRecv = setupSimpleHostAuth("pop3"); 208 pop3Account.mHostAuthSend = setupSimpleHostAuth("smtp"); 209 pop3Account.mSyncInterval = 90; 210 pop3Account.save(mMockContext); 211 212 // Setup the SyncReport's for these Accounts 213 MailService mailService = new MailService(); 214 mailService.mController = new TestController(mMockContext, getContext()); 215 try { 216 mailService.setupSyncReportsLocked(MailService.SYNC_REPORTS_RESET, mMockContext); 217 218 // Get back the map created by MailService 219 HashMap<Long, AccountSyncReport> syncReportMap = MailService.mSyncReports; 220 synchronized (syncReportMap) { 221 // Check the SyncReport's for correctness of sync interval 222 AccountSyncReport syncReport = syncReportMap.get(easAccount.mId); 223 assertNotNull(syncReport); 224 // EAS sync interval should have been changed to "never" 225 assertEquals(Account.CHECK_INTERVAL_NEVER, syncReport.syncInterval); 226 syncReport = syncReportMap.get(imapAccount.mId); 227 assertNotNull(syncReport); 228 assertEquals(60, syncReport.syncInterval); 229 syncReport = syncReportMap.get(pop3Account.mId); 230 assertNotNull(syncReport); 231 assertEquals(90, syncReport.syncInterval); 232 // Change the EAS account to push 233 ContentValues cv = new ContentValues(); 234 cv.put(Account.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH); 235 easAccount.update(mMockContext, cv); 236 syncReportMap.clear(); 237 mailService.setupSyncReportsLocked(easAccount.mId, mMockContext); 238 syncReport = syncReportMap.get(easAccount.mId); 239 assertNotNull(syncReport); 240 // EAS sync interval should be "never" in this case as well 241 assertEquals(Account.CHECK_INTERVAL_NEVER, syncReport.syncInterval); 242 } 243 } finally { 244 mailService.mController.cleanupForTest(); 245 } 246 } 247 248 /** 249 * Test that setupSyncReports will skip over poorly-formed accounts which can be left 250 * over after unit tests. 251 */ 252 public void testSetupSyncReportsWithBadAccounts() { 253 // Setup accounts that trigger each skip-over case 254 // 1: no email address 255 Account account1 = ProviderTestUtils.setupAccount("account1", false, mMockContext); 256 account1.mHostAuthRecv = setupSimpleHostAuth("imap"); 257 account1.mHostAuthSend = setupSimpleHostAuth("smtp"); 258 account1.mSyncInterval = 30; 259 account1.mEmailAddress = null; 260 account1.save(mMockContext); 261 // 2: no receiver hostauth 262 Account account2 = ProviderTestUtils.setupAccount("account2", false, mMockContext); 263 account2.mHostAuthRecv = null; 264 account2.mHostAuthSend = setupSimpleHostAuth("smtp"); 265 account2.mSyncInterval = 30; 266 account2.save(mMockContext); 267 // 3: no sender hostauth 268 Account account3 = ProviderTestUtils.setupAccount("account3", false, mMockContext); 269 account3.mHostAuthRecv = setupSimpleHostAuth("imap"); 270 account3.mHostAuthSend = null; 271 account3.mSyncInterval = 30; 272 account3.save(mMockContext); 273 274 // Setup the SyncReport's for these Accounts 275 MailService mailService = new MailService(); 276 mailService.mController = new TestController(mMockContext, getContext()); 277 try { 278 mailService.setupSyncReportsLocked(MailService.SYNC_REPORTS_RESET, mMockContext); 279 // Get back the map created by MailService - it should be empty 280 HashMap<Long, AccountSyncReport> syncReportMap = MailService.mSyncReports; 281 assertEquals(0, syncReportMap.size()); 282 } finally { 283 mailService.mController.cleanupForTest(); 284 } 285 286 } 287} 288